<commit_before>import Ember from 'ember';

export default Ember.Mixin.create({
  queryParams: ['sort'],
  sortDefaultValue: null,
  _userSettingsService: Ember.inject.service('user-settings-service'),
  sort: Ember.computed.oneWay('sortDefaultValue'),

  getNextSortDirection: function(currentDirection) {
    return currentDirection === 'asc' ? 'desc' : 'none';
  },

  computedSorting: Ember.computed('model.sorting', function() {
    var sorting = this.get('model.sorting');
    var result = {};

    if (sorting) {
      for (var i = 0; i < sorting.length; i++) {
        var propName = sorting[i].propName;
        var sortDef = {
          sortAscending: sorting[i].direction === 'asc' ? true : false,
          sortNumber: i + 1
        };
        result[propName] = sortDef;
      }
    }

    return result;
  }),

  /**
   * Производит операцию, обратную методу deserializeSortingParam.
   *
   * @param {Object} sorting Объект с параметрами сортировки, который нужно преобразовать в строку.
   * @returns {String} Результирующая строка.
   */
  serializeSortingParam: function(sorting) {
    return sorting.map(function(element) {
      return (element.direction === 'asc' ? '+' : '-') + element.propName;
    }).join('') || this.get('sortDefaultValue');
  },

  actions: {

    sortByColumn: function(column) {
      var propName = column.propName;
      var oldSorting = this.get('model.sorting');
      var newSorting = [];
      var sortDirection;
      if (oldSorting) {
        sortDirection = 'asc';
        for (var i = 0; i < oldSorting.length; i++) {
          if (oldSorting[i].propName === propName) {
            sortDirection = this.getNextSortDirection(oldSorting[i].direction);
            break;
          }
        }
      } else {
        sortDirection = 'asc';
      }

      if (sortDirection !== 'none') {
        newSorting.push({ propName: propName, direction: sortDirection });
      }

      let sortQueryParam = this.serializeSortingParam(newSorting);
      this.userSettings.sorting=newSorting;
      this.set('sort', sortQueryParam);
    },

    addColumnToSorting: function(column) {
      var propName = column.propName;
      var oldSorting = this.get('model.sorting');
      var newSorting = [];
      var changed = false;

      for (var i = 0; i < oldSorting.length; i++) {
        if (oldSorting[i].propName === propName) {
          var newDirection = this.getNextSortDirection(oldSorting[i].direction);
          if (newDirection !== 'none') {
            newSorting.push({ propName: propName, direction: newDirection });
          }

          changed = true;
        } else {
          newSorting.push(oldSorting[i]);
        }
      }

      if (!changed) {
        newSorting.push({ propName: propName, direction: 'asc' });
      }

      let sortQueryParam = this.serializeSortingParam(newSorting);
      this.userSettings.sorting=newSorting;
      this.set('sort', sortQueryParam);

    }
  }
});
<commit_msg>Add saving userSetting on tune sorting by hrader table click<commit_after>import Ember from 'ember';
const { getOwner } = Ember;

export default Ember.Mixin.create({
  queryParams: ['sort'],
  sortDefaultValue: null,
  sort: Ember.computed.oneWay('sortDefaultValue'),
  _userSettingsService: Ember.inject.service('user-settings-service'),
  _router:undefined,

  getNextSortDirection: function(currentDirection) {
    return currentDirection === 'asc' ? 'desc' : 'none';
  },

  computedSorting: Ember.computed('model.sorting', function() {
    var sorting = this.get('model.sorting');
    var result = {};

    if (sorting) {
      for (var i = 0; i < sorting.length; i++) {
        var propName = sorting[i].propName;
        var sortDef = {
          sortAscending: sorting[i].direction === 'asc' ? true : false,
          sortNumber: i + 1
        };
        result[propName] = sortDef;
      }
    }

    return result;
  }),

  /**
   * Производит операцию, обратную методу deserializeSortingParam.
   *
   * @param {Object} sorting Объект с параметрами сортировки, который нужно преобразовать в строку.
   * @returns {String} Результирующая строка.
   */
  serializeSortingParam: function(sorting) {
    return sorting.map(function(element) {
      return (element.direction === 'asc' ? '+' : '-') + element.propName;
    }).join('') || this.get('sortDefaultValue');
  },

  actions: {

    sortByColumn: function(column) {
      var propName = column.propName;
      var oldSorting = this.get('model.sorting');
      var newSorting = [];
      var sortDirection;
      if (oldSorting) {
        sortDirection = 'asc';
        for (var i = 0; i < oldSorting.length; i++) {
          if (oldSorting[i].propName === propName) {
            sortDirection = this.getNextSortDirection(oldSorting[i].direction);
            break;
          }
        }
      } else {
        sortDirection = 'asc';
      }

      if (sortDirection !== 'none') {
        newSorting.push({ propName: propName, direction: sortDirection });
      }

      let sortQueryParam = this.serializeSortingParam(newSorting);
      this.userSettings.sorting=newSorting;
      let router=getOwner(this).lookup('router:main');
      let moduleName =  router.currentRouteName;
      let savePromise=this.get('_userSettingsService').saveUserSetting({moduleName:moduleName,settingName:'DEFAULT',userSetting:{sorting:newSorting}});
      savePromise.then (
        record => {
          this.set('sort', sortQueryParam);
        }
      );
    },

    addColumnToSorting: function(column) {
      var propName = column.propName;
      var oldSorting = this.get('model.sorting');
      var newSorting = [];
      var changed = false;

      for (var i = 0; i < oldSorting.length; i++) {
        if (oldSorting[i].propName === propName) {
          var newDirection = this.getNextSortDirection(oldSorting[i].direction);
          if (newDirection !== 'none') {
            newSorting.push({ propName: propName, direction: newDirection });
          }

          changed = true;
        } else {
          newSorting.push(oldSorting[i]);
        }
      }

      if (!changed) {
        newSorting.push({ propName: propName, direction: 'asc' });
      }

      let sortQueryParam = this.serializeSortingParam(newSorting);
      this.userSettings.sorting=newSorting;
      let router=getOwner(this).lookup('router:main');
      let moduleName =  router.currentRouteName;
      let savePromise=this.get('_userSettingsService').saveUserSetting({moduleName:moduleName,settingName:'DEFAULT',userSetting:{sorting:newSorting}});
      savePromise.then (
        record => {
          this.set('sort', sortQueryParam);
        }
      );

    }
  }
});
<|endoftext|>


<commit_before>import templateUrl from './custom_button_details.html'

/** @ngInject */
export function CustomButtonDetailsState (routerHelper) {
  routerHelper.configureStates(getStates())
}

function getStates () {
  return {
    'services.custom_button_details': {
      url: '/:serviceId/custom_button_details',
      templateUrl,
      controller: StateController,
      controllerAs: 'vm',
      title: __('Custom Button Details'),
      params: {
        button: {
          value: null
        },
        serviceId: {
          value: null
        },
        vmId: {
          value: null
        }
      }
    }
  }
}

/** @ngInject */
function StateController ($state, $stateParams, CollectionsApi, EventNotifications, DialogFieldRefresh) {
  const vm = this
  vm.title = __('Custom button action')
  vm.dialogId = $stateParams.dialogId || ''
  vm.dialogs = {}
  vm.service = {}
  vm.serviceId = $stateParams.serviceId
  vm.vmId = $stateParams.vmId || null
  vm.button = $stateParams.button
  vm.resourceAction = vm.button.resource_action
  vm.submitCustomButton = submitCustomButton
  vm.submitButtonEnabled = false
  vm.dialogUrl = 'service_dialogs'
  vm.refreshField = refreshField
  vm.setDialogData = setDialogData
  vm.dialogData = {}
  vm.loading = true

  function init () {
    const options = {
      expand: 'resources',
      attributes: 'content',
      resource_action_id: vm.resourceAction.id,
      target_id: vm.serviceId,
      target_type: 'service'
    }

    const dialogId = vm.resourceAction.dialog_id
    const resolveDialogs = CollectionsApi.query(`service_dialogs/${dialogId}`, options)
    const resolveService = CollectionsApi.get('services', $stateParams.serviceId, {attributes: ['picture', 'picture.image_href']})

    Promise.all([resolveDialogs, resolveService]).then((data) => {
      const SERVICE_RESPONSE = 1
      const DIALOGS_RESPONSE = 0
      vm.dialogId = data[DIALOGS_RESPONSE].id
      vm.dialogs = data[DIALOGS_RESPONSE].content
      vm.service = data[SERVICE_RESPONSE]
      vm.loading = false
    })
  }
  init()
  function refreshField (field) {
    let targetType = 'service'
    let targetId = vm.serviceId
    if (vm.vmId) {
      targetType = 'vm'
      targetId = vm.vmId
    }

    let idList = {
      dialogId: vm.dialogId,
      resourceActionId: vm.resourceAction.id,
      targetId: targetId,
      targetType: targetType
    }

    return DialogFieldRefresh.refreshDialogField(vm.dialogData, [field.name], vm.dialogUrl, idList)
  }

  function setDialogData (data) {
    vm.submitButtonEnabled = data.validations.isValid
    vm.dialogData = data.data
  }

  function submitCustomButton () {
    let collection = 'services'
    let itemId = vm.serviceId

    if (vm.vmId) {
      collection = 'vms'
      itemId = vm.vmId
    }

    CollectionsApi.post(
      collection,
      itemId,
      {},
      angular.toJson({action: $stateParams.button.name, resource: vm.dialogData})
    ).then(submitSuccess, submitFailure)

    function submitSuccess (result) {
      EventNotifications.success(result.message)
      let stateName = 'services.details'
      let parameters = {serviceId: vm.serviceId}

      if (vm.vmId) {
        stateName = 'services.resource-details'
        parameters = {serviceId: vm.serviceId, vmId: vm.vmId}
      }
      $state.go(stateName, parameters)
    }

    function submitFailure (result) {
      EventNotifications.error(__('There was an error submitting this request: ') + result.data.error.message)
    }
  }
}
<commit_msg>VM custom button dialog - use the VM instead of the parent service<commit_after>import templateUrl from './custom_button_details.html'

/** @ngInject */
export function CustomButtonDetailsState (routerHelper) {
  routerHelper.configureStates(getStates())
}

function getStates () {
  return {
    'services.custom_button_details': {
      url: '/:serviceId/custom_button_details',
      templateUrl,
      controller: StateController,
      controllerAs: 'vm',
      title: __('Custom Button Details'),
      params: {
        button: {
          value: null
        },
        serviceId: {
          value: null
        },
        vmId: {
          value: null
        }
      }
    }
  }
}

/** @ngInject */
function StateController ($state, $stateParams, CollectionsApi, EventNotifications, DialogFieldRefresh) {
  const vm = this
  vm.title = __('Custom button action')
  vm.dialogId = $stateParams.dialogId || ''
  vm.dialogs = {}
  vm.service = {}
  vm.serviceId = $stateParams.serviceId
  vm.vmId = $stateParams.vmId || null
  vm.button = $stateParams.button
  vm.resourceAction = vm.button.resource_action
  vm.submitCustomButton = submitCustomButton
  vm.submitButtonEnabled = false
  vm.dialogUrl = 'service_dialogs'
  vm.refreshField = refreshField
  vm.setDialogData = setDialogData
  vm.dialogData = {}
  vm.loading = true

  function init () {
    const options = {
      expand: 'resources',
      attributes: 'content',
      resource_action_id: vm.resourceAction.id,
      target_id: vm.serviceId,
      target_type: 'service'
    }
    if (vm.vmId) {
      options.target_type = 'vm'
      options.target_id = vm.vmId
    }

    const dialogId = vm.resourceAction.dialog_id
    const resolveDialogs = CollectionsApi.query(`service_dialogs/${dialogId}`, options)
    const resolveService = CollectionsApi.get('services', $stateParams.serviceId, {attributes: ['picture', 'picture.image_href']})

    Promise.all([resolveDialogs, resolveService]).then((data) => {
      const SERVICE_RESPONSE = 1
      const DIALOGS_RESPONSE = 0
      vm.dialogId = data[DIALOGS_RESPONSE].id
      vm.dialogs = data[DIALOGS_RESPONSE].content
      vm.service = data[SERVICE_RESPONSE]
      vm.loading = false
    })
  }
  init()
  function refreshField (field) {
    let targetType = 'service'
    let targetId = vm.serviceId
    if (vm.vmId) {
      targetType = 'vm'
      targetId = vm.vmId
    }

    let idList = {
      dialogId: vm.dialogId,
      resourceActionId: vm.resourceAction.id,
      targetId: targetId,
      targetType: targetType
    }

    return DialogFieldRefresh.refreshDialogField(vm.dialogData, [field.name], vm.dialogUrl, idList)
  }

  function setDialogData (data) {
    vm.submitButtonEnabled = data.validations.isValid
    vm.dialogData = data.data
  }

  function submitCustomButton () {
    let collection = 'services'
    let itemId = vm.serviceId

    if (vm.vmId) {
      collection = 'vms'
      itemId = vm.vmId
    }

    CollectionsApi.post(
      collection,
      itemId,
      {},
      angular.toJson({action: $stateParams.button.name, resource: vm.dialogData})
    ).then(submitSuccess, submitFailure)

    function submitSuccess (result) {
      EventNotifications.success(result.message)
      let stateName = 'services.details'
      let parameters = {serviceId: vm.serviceId}

      if (vm.vmId) {
        stateName = 'services.resource-details'
        parameters = {serviceId: vm.serviceId, vmId: vm.vmId}
      }
      $state.go(stateName, parameters)
    }

    function submitFailure (result) {
      EventNotifications.error(__('There was an error submitting this request: ') + result.data.error.message)
    }
  }
}
<|endoftext|>


<commit_before>var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize-mysql').sequelize;
var _         = require('lodash');
var config    = require('./config');
var db        = {};

// TODO: add Heroku configuration

console.log('Initializing Sequelize');

// create your instance of sequelize
var sequelize = new Sequelize(config.db.name, config.db.username, config.db.password, {
  host: config.db.host,
  dialect: 'mysql',
  storage: config.db.storage
});

// loop through all files in models directory ignoring hidden files and this file
fs.readdirSync(config.modelsDir)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js')
  })
  // import model files and save model names
  .forEach(function(file) {
    console.log('Loading model file ' + file);
    var model = sequelize.import(path.join(config.modelsDir, file));
    db[model.name] = model;
  })

// invoke associations on each of the models
Object.keys(db).forEach(function(modelName) {
  if (db[modelName].options.hasOwnProperty('associate')) {
    db[modelName].options.associate(db)
  }
});

// Synchronizing any model changes with database.
// WARNING: this will DROP your database everytime you re-run your application
sequelize
  .sync({force: true})
  .complete(function(err){
    if(err) console.log("An error occured %j",err);
    else console.log("Database dropped and synchronized");
});

// assign the sequelize variables to the db object and returning the db.
module.exports = _.extend({
  sequelize: sequelize,
  Sequelize: Sequelize
}, db);<commit_msg>Database schema fixed<commit_after>var fs        = require('fs');
var path      = require('path');
var Sequelize = require('sequelize-mysql').sequelize;
var _         = require('lodash');
var config    = require('./config');
var db        = {};

// TODO: add Heroku configuration

console.log('Initializing Sequelize');

// create your instance of sequelize
var sequelize = new Sequelize(config.db.name, config.db.username, config.db.password, {
  host: config.db.host,
  dialect: 'mysql',
  storage: config.db.storage
});

// loop through all files in models directory ignoring hidden files and this file
fs.readdirSync(config.modelsDir)
  .filter(function(file) {
    return (file.indexOf('.') !== 0) && (file !== 'index.js')
  })
  // import model files and save model names
  .forEach(function(file) {
    console.log('Loading model file ' + file);
    var model = sequelize.import(path.join(config.modelsDir, file));
    db[model.name] = model;
  })

// invoke associations on each of the models
Object.keys(db).forEach(function(modelName) {
  if (db[modelName].options.hasOwnProperty('associate')) {
    db[modelName].options.associate(db)
  }
});

// Synchronizing any model changes with database.
// WARNING: this will DROP your database everytime you re-run your application
// sequelize
//   .sync({force: true})
//   .complete(function(err){
//     if(err) console.log("An error occured %j",err);
//     else console.log("Database dropped and synchronized");
// });

// assign the sequelize variables to the db object and returning the db.
module.exports = _.extend({
  sequelize: sequelize,
  Sequelize: Sequelize
}, db);<|endoftext|>


<commit_before>app.controller('UserCtrl', function ($scope, $http, ModalService, authService, mediaService, toastr) {
  $scope.expand = false;
  $scope.searchResults = [];
  $scope.isSearching = false;
  $scope.isLoading = false;
  $scope.playlists={
    'Default':{
      Name:"Default", Active:true, Contents:[]
    }
  };
  $scope.playlistName="Default";
  $scope.inQueue=false;
  $scope.queue =[];

  $http.get('/api/playlists').then(function(res){
    var data = res.data;
    if(data.Success){
      $scope.playlists = data.Playlists;
      for(pl in data.Playlists){
        if(pl.Active){
          $scope.playlistName = pl.Name;
          break;
        }
      }
    }
  });
  $scope.isAdding = false;
  $scope.userData;

  function addToQueue(callback){
    if($scope.playlists[$scope.playlistName].Contents.length <1){
      $scope.isAdding = false;
      return;
    }
    if(checkPresence()){
      $scope.isAdding = false;
      return;
    }
    var vidinfo = $scope.playlists[$scope.playlistName].Contents.shift();
    if($scope.queue.indexOf(vidinfo) > -1){
      $scope.adding=false;
      $scope.playlists[$scope.playlistName].Contents.unshift(vidinfo);
      return;
    }
    $http.post('/api/radio/queue', vidinfo)
    .then(function(res){
      var data = res.data;
      if(data.Success){
        $scope.playlists[$scope.playlistName].Contents.push(vidinfo);
        $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(){
          callback();
          $scope.isAdding = false;
        });
      }
      else{
        $scope.isAdding = false;
        callback(data.Error);
      }
    }, function(err){
      $scope.isAdding = false;
      callback(err);
    });
  };

  function checkPresence(){
    var present = false;
    var uname = authService.getUser().Username;
    $scope.queue.forEach(function(q_item){
      if(!q_item.DJ || q_item.DJ.toLowerCase() === uname.toLowerCase()){
        present = true;
      }
    });
    return present;
  }

  $scope.$on('queue_updated', function(event, queue){
    $scope.queue = queue;
    if(!$scope.inQueue || $scope.isAdding){
      return;
    }
    if(!checkPresence()){
      $scope.isAdding = true;
      addToQueue(function(err){
        return;
      });
    }
  });

  /*
  * Client Methods
  */
  $scope.search=function(){
    $scope.isSearching=true;
    $scope.isLoading = true;
    console.log('searching for ', $scope.searchCriteria.query);
    var cleancrit = encodeURIComponent($scope.searchCriteria.query)
    $http.get('/api/radio/search/'+cleancrit)
    .then(function(res){
      var data = res.data;
      if(data.Success){
        $scope.searchResults = data.Videos;
      }
      $scope.isLoading = false;
    },function(error){
      $scope.isLoading = false;
      console.log(error);
    });
  }

  $scope.viewPlaylist=function(name){
    $scope.playlistName=name;
    $scope.isSearching = false;
  }

  $scope.addToPlaylist=function(vidinfo){
    if(!vidinfo){
      return;
    }
    if($scope.playlists[$scope.playlistName].Contents.indexOf(vidinfo) > -1){
      toastr.error('Song already exists in playlist');
      return;
    }
    toastr.success('Song Added to Playlist: ' + $scope.playlistName);
    $scope.playlists[$scope.playlistName].Contents.push(vidinfo);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      console.log('playlist updated');
    });
  }

  $scope.removeFromPlaylist=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){
      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.moveUp=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $scope.playlists[$scope.playlistName].Contents.unshift(item);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){

      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.moveDown=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $scope.playlists[$scope.playlistName].Contents.push(item);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){

      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.joinLeaveQueue=function(){
    if($scope.inQueue){
      $scope.inQueue = false;
      return;
    }
    $scope.inQueue = true;
    addToQueue(function(err){});
  }

  $scope.login=function(){
    ModalService.showModal({
      templateUrl: "views/auth.html",
      controller: "AuthCtrl"
    }).then(function(modal) {
      modal.element.modal();
      modal.close.then(function(result) {});
    });
  };

  $scope.logOut=function(){
    authService.logOut();
  }

  $scope.getUser=function(){
    return authService.getUser();
  }
});
<commit_msg>fix playlists being lost.<commit_after>app.controller('UserCtrl', function ($scope, $http, ModalService, authService, mediaService, toastr) {
  $scope.expand = false;
  $scope.searchResults = [];
  $scope.isSearching = false;
  $scope.isLoading = false;
  $scope.playlists={
    'Default':{
      Name:"Default", Active:true, Contents:[]
    }
  };
  $scope.playlistName="Default";
  $scope.inQueue=false;
  $scope.queue =[];
  $scope.isAdding = false;
  $scope.userData;
  fetch_playlist();

  function fetch_playlist(){
    if(!authService.getUser()){
      return;
    }
    $http.get('/api/playlists').then(function(res){
      var data = res.data;
      console.log(data);
      if(data.Success){
        $scope.playlists = data.Playlists;
        for(pl in data.Playlists){
          if(pl.Active){
            $scope.playlistName = pl.Name;
            break;
          }
        }
      }
      else{
        toastr.error(data.Error, 'Problem retrieving playlist');
      }
    }, function(err){
      toastr.error(err, 'Problem retrieving playlist');
    });
  }

  function addToQueue(callback){
    if($scope.playlists[$scope.playlistName].Contents.length <1){
      $scope.isAdding = false;
      return;
    }
    if(checkPresence()){
      $scope.isAdding = false;
      return;
    }
    var vidinfo = $scope.playlists[$scope.playlistName].Contents.shift();
    if($scope.queue.indexOf(vidinfo) > -1){
      $scope.adding=false;
      $scope.playlists[$scope.playlistName].Contents.unshift(vidinfo);
      return;
    }
    $http.post('/api/radio/queue', vidinfo)
    .then(function(res){
      var data = res.data;
      if(data.Success){
        $scope.playlists[$scope.playlistName].Contents.push(vidinfo);
        $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(){
          callback();
          $scope.isAdding = false;
        });
      }
      else{
        $scope.isAdding = false;
        callback(data.Error);
      }
    }, function(err){
      $scope.isAdding = false;
      callback(err);
    });
  };

  function checkPresence(){
    var present = false;
    var uname = authService.getUser().Username;
    $scope.queue.forEach(function(q_item){
      if(!q_item.DJ || q_item.DJ.toLowerCase() === uname.toLowerCase()){
        present = true;
      }
    });
    return present;
  }

  $scope.$on('queue_updated', function(event, queue){
    $scope.queue = queue;
    if(!$scope.inQueue || $scope.isAdding){
      return;
    }
    if(!checkPresence()){
      $scope.isAdding = true;
      addToQueue(function(err){
        return;
      });
    }
  });

  /*
  * Client Methods
  */
  $scope.search=function(){
    $scope.isSearching=true;
    $scope.isLoading = true;
    console.log('searching for ', $scope.searchCriteria.query);
    var cleancrit = encodeURIComponent($scope.searchCriteria.query)
    $http.get('/api/radio/search/'+cleancrit)
    .then(function(res){
      var data = res.data;
      if(data.Success){
        $scope.searchResults = data.Videos;
      }
      $scope.isLoading = false;
    },function(error){
      $scope.isLoading = false;
      console.log(error);
    });
  }

  $scope.viewPlaylist=function(name){
    $scope.playlistName=name;
    $scope.isSearching = false;
  }

  $scope.addToPlaylist=function(vidinfo){
    if(!vidinfo){
      return;
    }
    if($scope.playlists[$scope.playlistName].Contents.indexOf(vidinfo) > -1){
      toastr.error('Song already exists in playlist');
      return;
    }
    toastr.success('Song Added to Playlist: ' + $scope.playlistName);
    $scope.playlists[$scope.playlistName].Contents.push(vidinfo);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      console.log('playlist updated');
    });
  }

  $scope.removeFromPlaylist=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){
      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.moveUp=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $scope.playlists[$scope.playlistName].Contents.unshift(item);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){

      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.moveDown=function(ind){
    var item = $scope.playlists[$scope.playlistName].Contents[ind];
    $scope.playlists[$scope.playlistName].Contents.splice(ind, 1);
    $scope.playlists[$scope.playlistName].Contents.push(item);
    $http.post('/api/playlists/update', $scope.playlists[$scope.playlistName]).then(function(res){
      var data = res.data;
      if(data.Success){

      }
      else{
        console.log(data.Error);
      }
    });
  }

  $scope.joinLeaveQueue=function(){
    if($scope.inQueue){
      $scope.inQueue = false;
      return;
    }
    $scope.inQueue = true;
    addToQueue(function(err){});
  }

  $scope.login=function(){
    ModalService.showModal({
      templateUrl: "views/auth.html",
      controller: "AuthCtrl"
    }).then(function(modal) {
      modal.element.modal();
      modal.close.then(function(result) {
        fetch_playlist();
      });
    });
  };

  $scope.logOut=function(){
    authService.logOut();
  }

  $scope.getUser=function(){
    return authService.getUser();
  }
});
<|endoftext|>


<commit_before>/**
 * Defines useful client-side functionality.
 */
Electron = {
  /**
   * @return {Boolean} `true` if the app is running in Electron, `false` otherwise.
   */
  isDesktop: function(){
    return /Electron/.test(navigator.userAgent);
  },

  /**
   * @return {Boolean} `true` if the app is running in Windows, `false` otherwise.
   */
  isWindows: function(){
    return /Windows NT/.test(navigator.userAgent);
  },

  // When the app is running in Electron, the following methods will be implemented by `preload.js`.
  // Stub them out in case the client tries to call them even when not running in Electron.

  /**
   * Open the given external protocol URL in the desktop's default manner. (For example, http(s):
   * URLs in the user's default browser.)
   *
   * @param {String} url - The URL to open.
   */
  openExternal: function() {},

  /**
   * Determines if the browser window is currently in fullscreen mode.
   *
   * "Fullscreen" here refers to the state triggered by toggling the native controls, not that
   * toggled by the HTML API.
   *
   * To detect when the browser window changes fullscreen state, observe the 'enter-full-screen'
   * and 'leave-full-screen' events using `onWindowEvent`.
   *
   * @return {Boolean} `true` if the browser window is in fullscreen mode, `false` otherwise.
   */
  isFullScreen: function() {},

  /**
   * Invokes _callback_ when the specified `BrowserWindow` event is fired.
   *
   * See https://github.com/atom/electron/blob/master/docs/api/browser-window.md#events for a list
   * of events.
   *
   * @param {String} event - The name of a `BrowserWindow` event.
   * @param {Function} callback - A function to invoke when `event` is triggered. Takes no arguments
   *   and returns no value.
   */
  onWindowEvent: function() {}
};

if (typeof ElectronImplementation !== 'undefined') {
  // The app is running in Electron. Merge the implementations from `preload.js`.
  _.extend(Electron, ElectronImplementation);
}
<commit_msg>Fix the client script's check for whether the app is running in Electron<commit_after>/**
 * Defines useful client-side functionality.
 */
Electron = {
  /**
   * @return {Boolean} `true` if the app is running in Electron, `false` otherwise.
   */
  isDesktop: function(){
    return /Electron/.test(navigator.userAgent);
  },

  /**
   * @return {Boolean} `true` if the app is running in Windows, `false` otherwise.
   */
  isWindows: function(){
    return /Windows NT/.test(navigator.userAgent);
  },

  // When the app is running in Electron, the following methods will be implemented by `preload.js`.
  // Stub them out in case the client tries to call them even when not running in Electron.

  /**
   * Open the given external protocol URL in the desktop's default manner. (For example, http(s):
   * URLs in the user's default browser.)
   *
   * @param {String} url - The URL to open.
   */
  openExternal: function() {},

  /**
   * Determines if the browser window is currently in fullscreen mode.
   *
   * "Fullscreen" here refers to the state triggered by toggling the native controls, not that
   * toggled by the HTML API.
   *
   * To detect when the browser window changes fullscreen state, observe the 'enter-full-screen'
   * and 'leave-full-screen' events using `onWindowEvent`.
   *
   * @return {Boolean} `true` if the browser window is in fullscreen mode, `false` otherwise.
   */
  isFullScreen: function() {},

  /**
   * Invokes _callback_ when the specified `BrowserWindow` event is fired.
   *
   * See https://github.com/atom/electron/blob/master/docs/api/browser-window.md#events for a list
   * of events.
   *
   * @param {String} event - The name of a `BrowserWindow` event.
   * @param {Function} callback - A function to invoke when `event` is triggered. Takes no arguments
   *   and returns no value.
   */
  onWindowEvent: function() {}
};

// Read `ElectronImplementation` from the window vs. doing `typeof ElectronImplementation` because
// Meteor will shadow it with a local variable in the latter case.
if (!_.isUndefined(window.ElectronImplementation)) {
  // The app is running in Electron. Merge the implementations from `preload.js`.
  _.extend(Electron, window.ElectronImplementation);
}
<|endoftext|>


<commit_before>/*
 * Ferret
 * Copyright (c) 2016 Yieldbot, Inc.
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint browser: true */
/* global document: false, $: false, Rx: false */
'use strict';

// Create the module
var app = function app() {

  if(typeof $ != "function" || typeof Rx != "object") {
    throw new Error("missing or invalid libraries (jQuery, Rx)");
  }

  // Init vars
  var serverUrl = location.protocol + '//' + location.hostname + ':' + location.port;

  // Get dom elements
  var $inputElm           = $('#searchInput'),
      $buttonElm          = $('#searchButton'),
      $resultsElm         = $('#searchResults'),
      $logoMain           = $("#logoMain"),
      $logoNavbarHolder   = $('#logoNavbarHolder'),
      $searchMain         = $("#searchMain"),
      $searchNavbarHolder = $("#searchNavbarHolder");

  // Encode HTML entity
  var encodeHtmlEntity = function(str) {
    return str.replace(/[\u00A0-\u9999\\<\>\&\'\"\\\/]/gim, function(c){
      return '&#' + c.charCodeAt(0) + ';' ;
    });
  };

  // init initializes the app
  function init() {

    // Create the observable from the input and click events
    var clickSource = Rx.Observable
      .fromEvent($buttonElm, 'click')
      .map(function() { return $inputElm.val(); });
    var inputSource = Rx.Observable
      .fromEvent($inputElm, 'keyup')
      .filter(function(e) { return (e.keyCode == 13); })
      .map(function(e) { return e.target.value; })
      .filter(function(text) { return text.length > 2; })
      .distinctUntilChanged()
      .throttle(1000);
    var observable = Rx.Observable.merge(clickSource, inputSource);

    // Iterate search providers
    [
      {name: 'answerhub', title: 'AnswerHub'},
      {name: 'github',    title: 'Github', keywordSuffix: '+extension:md'},
      {name: 'slack',     title: 'Slack'},
      {name: 'trello',    title: 'Trello'}
    ].forEach(function(provider) {
      observable.flatMapLatest(function(keyword) {
        beforeSearch();
        keyword = (provider.keywordSuffix) ? keyword + (''+provider.keywordSuffix) : keyword;
        return search(provider.name, keyword);
      })
      .subscribe(
        function(data) {
          afterSearch(null, {provider: provider, data: data});
        },
        function(err) {
          afterSearch(err, {provider: provider});
        }
      );
    });

    $inputElm.focus();
  }

  // search makes a search by the given provider and keyword
  function search(provider, keyword) {
    serverUrl = (location.protocol == 'file:') ? 'http://localhost:3030' : serverUrl; // for debug

    return $.ajax({
      url:      serverUrl+'/search',
      dataType: 'jsonp',
      method:   'GET',
      data: {
        provider: (''+provider),
        keyword:  (''+keyword),
        timeout:  '5000ms'
      }
    }).promise();
  }

  // beforeSearch prepares UI before search event
  function beforeSearch() {
    $logoMain.detach().appendTo($logoNavbarHolder).addClass('logo-navbar');
    $searchMain.detach().appendTo($searchNavbarHolder).addClass('input-group-search-navbar');
    $resultsElm.empty();
  }

  // afterSearch prepares UI after search event
  function afterSearch(err, result) {
    var provider = (result && result.provider && result.provider.title || 'unknown');

    if(err) {
      var errMsg = 'unknown error';
      if(typeof err == 'object') {
        errMsg = err.statusText || errMsg;
        if(typeof err.responseJSON == 'object') {
          errMsg = err.responseJSON.message || err.responseJSON.error || errMsg;
        }
      }
      $resultsElm.append($('
').text(provider));
      $resultsElm.append($('
').text('search failed due to ' + errMsg));
      return;
    }

    if(result && typeof result == 'object' && result.data instanceof Array) {
      $resultsElm.append($('
').text(provider));
      $resultsElm.append($.map(result.data, function (v) {
        var content  = ''+v.Title+'';
            content += '
';
            content += (v.Description) ? encodeHtmlEntity(v.Description)+'
' : '';
            content += (v.Date != "0001-01-01T00:00:00Z") ? ''+(''+(new Date(v.Date)).toISOString()).substr(0, 10)+'' : '';
            content += '
';

        return $('
').html(content);
      }));
      $resultsElm.append($('
'));
    }

    return;
  }

  // Return
  return {
    init: init
  };
};

$(document).ready(function() {
  app().init();
});<commit_msg>Implement dynamic provider list and refactoring<commit_after>/*
 * Ferret
 * Copyright (c) 2016 Yieldbot, Inc.
 * For the full copyright and license information, please view the LICENSE.txt file.
 */

/* jslint browser: true */
/* global document: false, $: false, Rx: false */
'use strict';

// Create the module
var app = function app() {

  if(typeof $ != "function" || typeof Rx != "object") {
    throw new Error("missing or invalid libraries (jQuery, Rx)");
  }

  // Init vars
  var serverUrl = location.protocol + '//' + location.hostname + ':' + location.port;
  serverUrl = (location.protocol == 'file:') ? 'http://localhost:3030' : serverUrl; // for debug

  // init initializes the app
  function init() {
    // Get providers
    getProviders().then(
      function(data) {
        if(data && data instanceof Array && data.length > 0) {
          // Listen for search requests
          listen(data);
        } else {
          critical("There is no any available provider to search");
        }
      },
      function(err) {
        var e = parseError(err);
        critical("Could not get the available providers due to " + e.message  + " (" + e.code + ")");
      }
    );
  }

  // listen listens search requests for the given providers
  function listen(providers) {
    // Create the observables

    // Click button
    var clickSource = Rx.Observable
      .fromEvent($('#searchButton'), 'click')
      .map(function() { return $('#searchInput').val(); });

    // Input field
    var inputSource = Rx.Observable
      .fromEvent($('#searchInput'), 'keyup')
      .filter(function(e) { return (e.keyCode == 13); })
      .map(function(e) { return e.target.value; })
      .filter(function(text) { return text.length > 2; })
      .distinctUntilChanged()
      .throttle(1000);

    // Merge observables
    var observable = Rx.Observable.merge(clickSource, inputSource);

    // Check providers
    if(providers instanceof Array && providers.length > 0) {

      // Iterate providers
      providers.forEach(function(provider) {
        observable
          .flatMapLatest(function(keyword) {
            // Prepare UI
            $("#logoMain").detach().appendTo($('#logoNavbarHolder')).addClass('logo-navbar');
            $("#searchMain").detach().appendTo($("#searchNavbarHolder")).addClass('input-group-search-navbar');
            $('#searchResults').empty();

            // Exceptions
            keyword = (provider.name == "github") ? keyword+'+extension:md' : keyword;

            // Search
            return search(provider.name, keyword);
          })
          .subscribe(
            function(data) {
              if(data && data instanceof Array) {
                $('#searchResults').append($('
').text((provider.title || '')));
                $('#searchResults').append($.map(data, function (v) {
                  var content  = ''+v.Title+'';
                      content += '
';
                      content += (v.Description) ? encodeHtmlEntity(v.Description)+'
' : '';
                      content += (v.Date != "0001-01-01T00:00:00Z") ? ''+(''+(new Date(v.Date)).toISOString()).substr(0, 10)+'' : '';
                      content += '
';

                  return $('
').html(content);
                }));
                $('#searchResults').append($('
'));
              }
            },
            function(err) {
              var e = parseError(err);
              $('#searchResults').append($('
').text(provider));
              $('#searchResults').append($('
').text(e.message));
            }
          );
      });
    }

    $('#searchInput').focus();
  }

  // getProviders gets the provider list
  function getProviders() {
    return $.ajax({
      url:      serverUrl+'/providers',
      dataType: 'jsonp',
      method:   'GET',
    }).promise();
  }

  // search makes a search by the given provider and keyword
  function search(provider, keyword) {
    return $.ajax({
      url:      serverUrl+'/search',
      dataType: 'jsonp',
      method:   'GET',
      data: {
        provider: (''+provider),
        keyword:  (''+keyword),
        timeout:  '5000ms'
      }
    }).promise();
  }

  // warning shows a warning message
  function warning(message) {
    $('#searchAlerts')
      .html($('
')
        .text(message));
  }

  // critical shows a critical message
  function critical(message) {
    $('#searchAlerts')
      .html($('
')
        .text(message));
  }

  // parseError parses the given error message and returns an object
  function parseError(err) {
    var code    = 0,
        message = 'unknown error';

    if(err && typeof err == 'object') {
      code    = err.status || code;
      message = err.statusText || err.message || message;
      if(typeof err.responseJSON == 'object') {
        message = err.responseJSON.message || err.responseJSON.error || message;
      }
    }

    return {code: code, message: message}
  }

  // encodeHtmlEntity encodes HTML entity
  function encodeHtmlEntity(str) {
    return str.replace(/[\u00A0-\u9999\\<\>\&\'\"\\\/]/gim, function(c){
      return '&#' + c.charCodeAt(0) + ';' ;
    });
  }

  // Return
  return {
    init: init
  };
};

$(document).ready(function() {
  app().init();
});<|endoftext|>


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

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

// ************************************************************************************************
// Constants

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

const httpObserver = Cc["@joehewitt.com/firebug-http-observer;1"].getService(Ci.nsIObserverService);

// List of text content types. These content-types are cached.
const contentTypes =
{
    "text/plain": 1,
    "text/html": 1,
    "text/xml": 1,
    "text/css": 1,
    "text/sgml": 1,
    "text/rtf": 1,
    "text/richtext": 1,
    "text/x-setext": 1,
    "text/rtf": 1,
    "text/tab-separated-values": 1,
    "application/x-javascript": 1,
    "application/x-httpd-php": 1,
};

// ************************************************************************************************
// Model implementation

/**
 * Implementation of cache model. The only purpose of this object is to register an HTTP
 * observer so, HTTP communication can be interecepted and all incoming data stored within
 * a cache.
 */
Firebug.TabCacheModel = extend(Firebug.Module,
{
    initializeUI: function(owner)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.sysout("tabCache. Cache model initialized.");

        // Register for HTTP events.
        if (Ci.nsITraceableChannel)
            httpObserver.addObserver(this, "firebug-http-event", false);
    },

    shutdown: function()
    {
        if (Ci.nsITraceableChannel)
            httpObserver.removeObserver(this, "firebug-http-event");
    },

    initContext: function(context)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.dumpProperties("tabCache.initContext for: " + context.window.location.href);
    },

    /* nsIObserver */
    observe: function(subject, topic, data)
    {
        try
        {
            if (!(subject instanceof Ci.nsIHttpChannel))
                return;

            var win = getWindowForRequest(subject);
            var tabId = Firebug.getTabIdForWindow(win);
            if (!(tabId && win))
                return;

            if (topic == "http-on-modify-request")
                this.onModifyRequest(subject, win, tabId);
            else if (topic == "http-on-examine-response")
                this.onExamineResponse(subject, win, tabId);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.sysout("tabCache.observe EXCEPTION", err);
        }
    },

    onModifyRequest: function(request, win, tabId)
    {
    },

    onExamineResponse: function(request, win, tabId)
    {
        try
        {
            // Register traceable channel listener in order to intercept all incoming data for
            // this context/tab. nsITraceableChannel interface is introduced in Firefox 3.0.4
            request.QueryInterface(Ci.nsITraceableChannel);
            var newListener = new TracingListener(win);
            newListener.listener = request.setNewListener(newListener);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache: Register Traceable Listener EXCEPTION", err);
        }
    },
});

// ************************************************************************************************

/**
 * This cache object is intended to cache all responses made by a specific tab.
 * The implementation is based on nsITraceableChannel interface introduced in
 * Firefox 3.0.4. This interface allows to intercept all incoming HTTP data.
 *
 * This object replaces the SourceCache, which still exist only for backward
 * compatibility.
 *
 * The object is derived from SourceCache so, the same interface and most of the
 * implementation is used.
 */
Firebug.TabCache = function(win)
{
    if (FBTrace.DBG_CACHE)
        FBTrace.dumpProperties("tabCache.TabCache Created for: " + win.location.href);

    Firebug.SourceCache.call(this, win, null);
};

Firebug.TabCache.prototype = extend(Firebug.SourceCache.prototype,
{
    listeners: [],
    requests: [],       // requests in progress.

    storePartialResponse: function(request, responseText)
    {
        var url = safeGetName(request);

        if (!this.requests[url])
        {
            this.invalidate(url);
            this.requests[url] = request;
        }

        // Convert
        responseText = FBL.convertToUnicode(responseText);

        // Store partial content into the cache.
        this.store(url, responseText);
    },

    stopRequest: function(request)
    {
        var url = safeGetName(request);
        delete this.requests[url];

        // Notify listeners.
        dispatch(this.listeners, "onStoreResponse", [this.window, request, this.cache[url]]);
    },

    storeSplitLines: function(url, lines)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.sysout("tabCache.storeSplitLines: " + url, lines);

        var currLines = this.cache[url];
        if (!currLines)
            currLines = this.cache[url] = [];

        // Join the last line with the new first one so, the source code
        // lines are properly formatted.
        if (currLines.length)
            currLines[currLines.length-1] += lines.shift();

        // Append new lines (if any) into the array for specified url.
        if (lines.length)
            this.cache[url] = currLines.concat(lines);

    	return this.cache[url];
    },

    loadFromCache: function(url, method, file)
    {
        // The ancestor implementation (SourceCache) uses ioService.newChannel, which
        // can result in additional request to the server (in case the response can't
        // be loaded from the Firefox cache) - known as double-load problem.
        // This new implementation (TabCache) uses nsITraceableListener so, all responses
        // should be already cached.

        if (FBTrace.DBG_CACHE)
            FBTrace.dumpProperties("tabCache.loadFromCache: FAILED " +
                this.window.location.href, this.cache);
    },

    // Listeners
    addListener: function(listener)
    {
        this.listeners.push(listener);
    },

    removeListener: function(listener)
    {
        remove(this.listeners, listener);
    }
});

// ************************************************************************************************
// TracingListener implementation

/**
 * This object implements nsIStreamListener interface and is intended to monitor all network
 * channels (nsIHttpChannel). For every channel a new instance of this object is created and
 * registered. See Firebug.TabCacheModel.onExamineResponse method.
 */
function TracingListener(win)
{
    this.window = win;
    this.listener = null;
    this.endOfLine = false;
}

TracingListener.prototype =
{
    onCollectData: function(request, inputStream, offset, count)
    {
        try
        {
            var binaryInputStream = CCIN("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
            var storageStream = CCIN("@mozilla.org/storagestream;1", "nsIStorageStream");
            var binaryOutputStream = CCIN("@mozilla.org/binaryoutputstream;1", "nsIBinaryOutputStream");

            binaryInputStream.setInputStream(inputStream);
            storageStream.init(8192, count, null);
            binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));

            var data = binaryInputStream.readBytes(count);
            binaryOutputStream.writeBytes(data, count);

            // Avoid creating additional empty line if response comes in more pieces
            // and the split is made just between "\r" and "\n" (Win line-end).
            // So, if the response starts with "\n" while the previous part ended with "\r",
            // remove the first character.
            if (this.endOfLine && data.length && data[0] == "\n")
                data = data.substring(1);

            if (data.length)
                this.endOfLine = data[data.length-1] == "\r";

            // At this moment, initContext is alredy called so, the context is
            // ready and associated with the window.
            var context = TabWatcher.getContextByWindow(this.window);
            if (context)
            {
                // Store received data into the cache as they come.
                context.sourceCache.storePartialResponse(request, data);
            }
            else
            {
                if (FBTrace.DBG_CACHE)
                    FBTrace.dumpProperties("tabCache.onCollectData NO CONTEXT for: " + this.window.location.href);
            }

            // Let other listeners use the stream.
            return storageStream.newInputStream(0);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onCollectData EXCEPTION\n", err);
        }

        return null;
    },

    /* nsIStreamListener */
    onDataAvailable: function(request, requestContext, inputStream, offset, count)
    {
        // Cache only text responses for now.
        if (contentTypes[request.contentType])
        {
            var newStream = this.onCollectData(request, inputStream, offset, count);
            if (newStream)
                inputStream = newStream;
        }
        else
        {
            if (FBTrace.DBG_CACHE)
                FBTrace.dumpProperties("tabCache.onDataAvailable Content-Type not cached: " +
                    request.contentType + ", " + safeGetName(request));
        }

        try
        {
            if (this.listener)
                this.listener.onDataAvailable(request, requestContext, inputStream, offset, count);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onDataAvailable" +
                    "(" + request + ", " + requestContext + ", " +
                    inputStream + ", " + offset + ", " + count + ") EXCEPTION: " +
                    safeGetName(request), err);
        }
    },

    onStartRequest: function(request, requestContext)
    {
        try
        {
            if (this.listener)
                this.listener.onStartRequest(request, requestContext);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onStartRequest EXCEPTION\n", err);
        }
    },

    onStopRequest: function(request, requestContext, statusCode)
    {
        try
        {
            var context = TabWatcher.getContextByWindow(this.window);
            if (context)
            {
                context.sourceCache.stopRequest(request);
            }
            else
            {
                if (FBTrace.DBG_CACHE)
                    FBTrace.dumpProperties("tabCache.onStopRequest NO CONTEXT for: " + this.window.location.href);
            }

            if (this.listener)
                this.listener.onStopRequest(request, requestContext, statusCode);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onStopRequest EXCEPTION\n", err);
        }
    },

    /* nsISupports */
    QueryInterface: function(iid)
    {
        if (iid.equals(Ci.nsIStreamListener) ||
            iid.equals(Ci.nsISupportsWeakReference) ||
            iid.equals(Ci.nsISupports))
        {
            return this;
        }

        throw Components.results.NS_NOINTERFACE;
    }
}

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

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

    return null;
}

// ************************************************************************************************
// Registration

Firebug.registerModule(Firebug.TabCacheModel);

// ************************************************************************************************

}});
<commit_msg>Another content type that should be cached.<commit_after>/* See license.txt for terms of usage */

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

// ************************************************************************************************
// Constants

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

const httpObserver = Cc["@joehewitt.com/firebug-http-observer;1"].getService(Ci.nsIObserverService);

// List of text content types. These content-types are cached.
const contentTypes =
{
    "text/plain": 1,
    "text/html": 1,
    "text/xml": 1,
    "text/css": 1,
    "text/sgml": 1,
    "text/rtf": 1,
    "text/richtext": 1,
    "text/x-setext": 1,
    "text/rtf": 1,
    "text/javascript": 1,
    "text/tab-separated-values": 1,
    "application/x-javascript": 1,
    "application/x-httpd-php": 1,
};

// ************************************************************************************************
// Model implementation

/**
 * Implementation of cache model. The only purpose of this object is to register an HTTP
 * observer so, HTTP communication can be interecepted and all incoming data stored within
 * a cache.
 */
Firebug.TabCacheModel = extend(Firebug.Module,
{
    initializeUI: function(owner)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.sysout("tabCache. Cache model initialized.");

        // Register for HTTP events.
        if (Ci.nsITraceableChannel)
            httpObserver.addObserver(this, "firebug-http-event", false);
    },

    shutdown: function()
    {
        if (Ci.nsITraceableChannel)
            httpObserver.removeObserver(this, "firebug-http-event");
    },

    initContext: function(context)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.dumpProperties("tabCache.initContext for: " + context.window.location.href);
    },

    /* nsIObserver */
    observe: function(subject, topic, data)
    {
        try
        {
            if (!(subject instanceof Ci.nsIHttpChannel))
                return;

            var win = getWindowForRequest(subject);
            var tabId = Firebug.getTabIdForWindow(win);
            if (!(tabId && win))
                return;

            if (topic == "http-on-modify-request")
                this.onModifyRequest(subject, win, tabId);
            else if (topic == "http-on-examine-response")
                this.onExamineResponse(subject, win, tabId);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.sysout("tabCache.observe EXCEPTION", err);
        }
    },

    onModifyRequest: function(request, win, tabId)
    {
    },

    onExamineResponse: function(request, win, tabId)
    {
        try
        {
            // Register traceable channel listener in order to intercept all incoming data for
            // this context/tab. nsITraceableChannel interface is introduced in Firefox 3.0.4
            request.QueryInterface(Ci.nsITraceableChannel);
            var newListener = new TracingListener(win);
            newListener.listener = request.setNewListener(newListener);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache: Register Traceable Listener EXCEPTION", err);
        }
    },
});

// ************************************************************************************************

/**
 * This cache object is intended to cache all responses made by a specific tab.
 * The implementation is based on nsITraceableChannel interface introduced in
 * Firefox 3.0.4. This interface allows to intercept all incoming HTTP data.
 *
 * This object replaces the SourceCache, which still exist only for backward
 * compatibility.
 *
 * The object is derived from SourceCache so, the same interface and most of the
 * implementation is used.
 */
Firebug.TabCache = function(win)
{
    if (FBTrace.DBG_CACHE)
        FBTrace.dumpProperties("tabCache.TabCache Created for: " + win.location.href);

    Firebug.SourceCache.call(this, win, null);
};

Firebug.TabCache.prototype = extend(Firebug.SourceCache.prototype,
{
    listeners: [],
    requests: [],       // requests in progress.

    storePartialResponse: function(request, responseText)
    {
        var url = safeGetName(request);

        if (!this.requests[url])
        {
            this.invalidate(url);
            this.requests[url] = request;
        }

        // Convert
        responseText = FBL.convertToUnicode(responseText);

        // Store partial content into the cache.
        this.store(url, responseText);
    },

    stopRequest: function(request)
    {
        var url = safeGetName(request);
        delete this.requests[url];

        // Notify listeners.
        dispatch(this.listeners, "onStoreResponse", [this.window, request, this.cache[url]]);
    },

    storeSplitLines: function(url, lines)
    {
        if (FBTrace.DBG_CACHE)
            FBTrace.sysout("tabCache.storeSplitLines: " + url, lines);

        var currLines = this.cache[url];
        if (!currLines)
            currLines = this.cache[url] = [];

        // Join the last line with the new first one so, the source code
        // lines are properly formatted.
        if (currLines.length)
            currLines[currLines.length-1] += lines.shift();

        // Append new lines (if any) into the array for specified url.
        if (lines.length)
            this.cache[url] = currLines.concat(lines);

    	return this.cache[url];
    },

    loadFromCache: function(url, method, file)
    {
        // The ancestor implementation (SourceCache) uses ioService.newChannel, which
        // can result in additional request to the server (in case the response can't
        // be loaded from the Firefox cache) - known as double-load problem.
        // This new implementation (TabCache) uses nsITraceableListener so, all responses
        // should be already cached.

        if (FBTrace.DBG_CACHE)
            FBTrace.dumpProperties("tabCache.loadFromCache: FAILED " +
                this.window.location.href, this.cache);
    },

    // Listeners
    addListener: function(listener)
    {
        this.listeners.push(listener);
    },

    removeListener: function(listener)
    {
        remove(this.listeners, listener);
    }
});

// ************************************************************************************************
// TracingListener implementation

/**
 * This object implements nsIStreamListener interface and is intended to monitor all network
 * channels (nsIHttpChannel). For every channel a new instance of this object is created and
 * registered. See Firebug.TabCacheModel.onExamineResponse method.
 */
function TracingListener(win)
{
    this.window = win;
    this.listener = null;
    this.endOfLine = false;
}

TracingListener.prototype =
{
    onCollectData: function(request, inputStream, offset, count)
    {
        try
        {
            var binaryInputStream = CCIN("@mozilla.org/binaryinputstream;1", "nsIBinaryInputStream");
            var storageStream = CCIN("@mozilla.org/storagestream;1", "nsIStorageStream");
            var binaryOutputStream = CCIN("@mozilla.org/binaryoutputstream;1", "nsIBinaryOutputStream");

            binaryInputStream.setInputStream(inputStream);
            storageStream.init(8192, count, null);
            binaryOutputStream.setOutputStream(storageStream.getOutputStream(0));

            var data = binaryInputStream.readBytes(count);
            binaryOutputStream.writeBytes(data, count);

            // Avoid creating additional empty line if response comes in more pieces
            // and the split is made just between "\r" and "\n" (Win line-end).
            // So, if the response starts with "\n" while the previous part ended with "\r",
            // remove the first character.
            if (this.endOfLine && data.length && data[0] == "\n")
                data = data.substring(1);

            if (data.length)
                this.endOfLine = data[data.length-1] == "\r";

            // At this moment, initContext is alredy called so, the context is
            // ready and associated with the window.
            var context = TabWatcher.getContextByWindow(this.window);
            if (context)
            {
                // Store received data into the cache as they come.
                context.sourceCache.storePartialResponse(request, data);
            }
            else
            {
                if (FBTrace.DBG_CACHE)
                    FBTrace.dumpProperties("tabCache.onCollectData NO CONTEXT for: " + this.window.location.href);
            }

            // Let other listeners use the stream.
            return storageStream.newInputStream(0);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onCollectData EXCEPTION\n", err);
        }

        return null;
    },

    /* nsIStreamListener */
    onDataAvailable: function(request, requestContext, inputStream, offset, count)
    {
        // Cache only text responses for now.
        if (contentTypes[request.contentType])
        {
            var newStream = this.onCollectData(request, inputStream, offset, count);
            if (newStream)
                inputStream = newStream;
        }
        else
        {
            if (FBTrace.DBG_CACHE)
                FBTrace.dumpProperties("tabCache.onDataAvailable Content-Type not cached: " +
                    request.contentType + ", " + safeGetName(request));
        }

        try
        {
            if (this.listener)
                this.listener.onDataAvailable(request, requestContext, inputStream, offset, count);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onDataAvailable" +
                    "(" + request + ", " + requestContext + ", " +
                    inputStream + ", " + offset + ", " + count + ") EXCEPTION: " +
                    safeGetName(request), err);
        }
    },

    onStartRequest: function(request, requestContext)
    {
        try
        {
            if (this.listener)
                this.listener.onStartRequest(request, requestContext);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onStartRequest EXCEPTION\n", err);
        }
    },

    onStopRequest: function(request, requestContext, statusCode)
    {
        try
        {
            var context = TabWatcher.getContextByWindow(this.window);
            if (context)
            {
                context.sourceCache.stopRequest(request);
            }
            else
            {
                if (FBTrace.DBG_CACHE)
                    FBTrace.dumpProperties("tabCache.onStopRequest NO CONTEXT for: " + this.window.location.href);
            }

            if (this.listener)
                this.listener.onStopRequest(request, requestContext, statusCode);
        }
        catch (err)
        {
            if (FBTrace.DBG_ERRORS)
                FBTrace.dumpProperties("tabCache.TracingListener.onStopRequest EXCEPTION\n", err);
        }
    },

    /* nsISupports */
    QueryInterface: function(iid)
    {
        if (iid.equals(Ci.nsIStreamListener) ||
            iid.equals(Ci.nsISupportsWeakReference) ||
            iid.equals(Ci.nsISupports))
        {
            return this;
        }

        throw Components.results.NS_NOINTERFACE;
    }
}

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

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

    return null;
}

// ************************************************************************************************
// Registration

Firebug.registerModule(Firebug.TabCacheModel);

// ************************************************************************************************

}});
<|endoftext|>


<commit_before>describe('Basic Logging', function () {
  var app;

  // Log with trace: true.
  describe('with tracing', function () {

    before(function (done) {
      app = require('cantina');
      app.boot(function (err) {
        assert.ifError(err);
        app.conf.add({
          log: {
            trace: true
          }
        });
        done();
      });
    });

    after(function (done) {
      app.destroy(done);
    });

    it('logs with correct callsite', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.msg, 'test');
          assert.equal(obj.src.file, 'test/basic.js');
          assert.equal(obj.src.line, 38);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        assert.ifError(err);
        app.log('test');
      });
    });
  });

  // Log with trace: false.
  describe('with no tracing', function () {

    beforeEach(function (done) {
      app = require('cantina');
      app.boot(function (err) {
        assert.ifError(err);
        app.conf.add({
          log: {
            trace: false
          }
        });
        done();
      });
    });

    afterEach(function (done) {
      app.destroy(done);
    });

    it('logs with correct callsite', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.msg, 'test');
          assert.equal(obj.src, undefined);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        assert.ifError(err);
        app.log('test');
      });
    });

    it('logs with data', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.type, 'test with data');
          assert.equal(obj.a, 'A');
          assert.equal(obj.b, 'B');
          assert(obj.timestamp);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        app.log('test with data', {a: 'A', b: 'B'});
      });
    });

    it('logs a formatted string', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.type, 'msg');
          assert.equal(obj.msg, 'name: Brian Link');
          done();
        }
      };

      require('../');

      app.start(function (err) {
        app.log('name: %s %s', 'Brian', 'Link');
      });
    });
  });
});<commit_msg>Add failing test<commit_after>describe('Basic Logging', function () {
  var app;

  // Log with trace: true.
  describe('with tracing', function () {

    before(function (done) {
      app = require('cantina');
      app.boot(function (err) {
        assert.ifError(err);
        app.conf.add({
          log: {
            trace: true
          }
        });
        done();
      });
    });

    after(function (done) {
      app.destroy(done);
    });

    it('logs with correct callsite', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.msg, 'test');
          assert.equal(obj.src.file, 'test/basic.js');
          assert.equal(obj.src.line, 38);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        assert.ifError(err);
        app.log('test');
      });
    });
  });

  // Log with trace: false.
  describe('with no tracing', function () {

    beforeEach(function (done) {
      app = require('cantina');
      app.boot(function (err) {
        assert.ifError(err);
        app.conf.add({
          log: {
            trace: false
          }
        });
        done();
      });
    });

    afterEach(function (done) {
      app.destroy(done);
    });

    it('logs with correct callsite', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.msg, 'test');
          assert.equal(obj.src, undefined);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        assert.ifError(err);
        app.log('test');
      });
    });

    it('logs with data', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.type, 'test with data');
          assert.equal(obj.a, 'A');
          assert.equal(obj.b, 'B');
          assert(obj.timestamp);
          done();
        }
      };

      require('../');

      app.start(function (err) {
        app.log('test with data', {a: 'A', b: 'B'});
      });
    });

    it('logs a formatted string', function (done) {
      app.loggerStore = {
        add: function (obj) {
          assert.equal(obj.type, 'msg');
          assert.equal(obj.msg, 'name: Brian Link');
          done();
        }
      };

      require('../');

      app.start(function (err) {
        app.log('name: %s %s', 'Brian', 'Link');
      });
    });

    it('logs data having a toJSON method', function (done) {
      app.loggerStore = {
        add: function (obj) {
          obj = JSON.parse(JSON.stringify(obj));
          assert.equal(obj.type, 'test data having toJSON');
          assert.equal(obj.a, 'A');
          assert.equal(obj.b, 'B');
          assert.equal(obj.ab, 'AB');
          assert(obj.timestamp);
          done();
        }
      };

      require('../');
      var data = {
        a: 'A',
        b: 'B',
        toJSON: function () {
          return {
            a: this.a,
            b: this.b,
            ab: this.a + this.b
          }
        }
      }

      app.start(function (err) {
        app.log('test data having toJSON', data);
      });
    });
  });
});<|endoftext|>


<commit_before>/**
 * Defines common questions that are repeated across surveys.
 */

var commonQuestions = {};

/**
 * What website were you trying to visit when the page was displayed?
 * @returns {object} The DOM subtree with the question.
*/
commonQuestions.createWhatWebsiteQuestion = function() {
  var whatWebsite = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What website were you trying to visit when the page was displayed?',
      true);
  return whatWebsite;
};

/**
 * How did you choose between the two options? Note that the options should be
 * flipped for the proceed & no-proceed scenarios.
 * @param {string} The option that the user chose.
 * @param {string} The option that the user didn't choose.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createChoiceQuestion = function(chosen, alternative) {
  var howChoose = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'You chose "' + chosen + '" instead of "' + alternative + '."' +
          ' How did you choose between the two options?',
      true);
  return howChoose;
};

/**
 * Why did you choose not to proceed to <website>?
 * @param {string} The option that the user may have chosen,
 *     without enclosing quotes.
 * @returns {object} The DOM subtree with the question.
*/
commonQuestions.createNotProceedChoiceQuestion = function(chosen) {
  var howChoose = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'You chose the "' + chosen + '" option, or you closed the page.' +
      ' Why did you choose not to proceed to ' + surveyDriver.questionUrl +
      '?', true);
  howChoose.setPlaceholder(
      'You chose the "' + chosen + '" option, or you closed the page.' +
      ' Why did you choose not to proceed to [URL]?');
  return howChoose;
};

/**
 * What was the page trying to tell you?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createPageMeaningQuestion = function() {
  var meaning = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What was the page trying to tell you, in your own words?',
      true);
  return meaning;
};

/**
 * What was the dialog trying to tell you?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogMeaningQuestion = function() {
  var meaning = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What was the dialog trying to tell you, in your own words?',
      true);
  return meaning;
};

/**
 * Who do you think the page was from?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createPageSourceQuestion = function() {
  var source = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Who was the page from?',
      true,
      [
        'Chrome (my browser)',
        'A hacker',
        prettyPrintOS(),
        surveyDriver.questionUrl,
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return source;
};

/**
 * Who do you think the dialog was from?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogSourceQuestion = function() {
  var source = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Who was the dialog from?',
      true,
      [
        'Chrome (my browser)',
        'A hacker',
        prettyPrintOS(),
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return source;
};

/**
 * Have you visited example.com before?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createHistoryQuestions = function() {
  var hist = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Have you visited ' + surveyDriver.questionUrl + ' before?',
      true,
      ['Yes', 'No', 'I don\'t know'],
      constants.Randomize.ANCHOR_LAST);
  hist.setPlaceholder('Have you visited [URL] before?');
  var errorBefore = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Have you seen a page like the one pictured above when trying to visit ' +
          surveyDriver.questionUrl + ' before?',
      true,
      ['Yes', 'No', 'I don\'t know'],
      constants.Randomize.ANCHOR_LAST);
  errorBefore.setPlaceholder(
      'Have you seen a page like the one pictured above when trying to visit ' +
      '[URL] before?');
  hist.addDependentQuestion(errorBefore, 'Yes');
  return hist;
};

/**
 * What led you to the page?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createReferrerQuestion = function() {
  var referrer = new FixedQuestion(
      constants.QuestionType.RADIO,
      'What led you to the page?',
      true,
      [
        'Entered or typed a URL',
        'Used a search engine',
        'Clicked link from an email message',
        'Clicked link in a chat window',
        'Clicked link on a web page',
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return referrer;
};

/**
 * What led you to install the extension or app mentioned in the dialog?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogReferrerQuestion = function() {
  var referrer = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What led you to try to install the extension or app mentioned' +
      ' in the dialog?',
      true);
  return referrer;
};

/**
 * Do you have an account on example.com?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createAccountQuestion = function() {
  var account = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Do you have an account on ' + surveyDriver.questionUrl + '?',
      true,
      ['Yes', 'No', 'I\'m not sure', 'I prefer not to answer'],
      constants.Randomize.ANCHOR_LAST);
  account.setPlaceholder('Do you have an account on [URL]?');
  return account;
};

/**
 * Were you trying to visit example.com when you saw the page instead?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createVisitQuestion = function() {
  var visit = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Were you trying to visit ' + surveyDriver.questionUrl +
           ' when you saw the page instead?',
      true,
      ['Yes', 'No', 'I\'m not sure', 'I prefer not to answer'],
      constants.Randomize.ANCHOR_LAST);
  visit.setPlaceholder('Were you trying to visit [URL] when you saw the page ' +
      'instead?');
  return visit;
};

/**
 * How much do you trust example.com?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createTrustQuestion = function() {
  var trust = new ScaleQuestion(
      constants.QuestionType.VERTICAL_SCALE,
      'How much do you trust ' + surveyDriver.questionUrl + '?',
      true,
      [
        'Strongly distrust',
        'Somewhat distrust',
        'Neither trust nor distrust',
        'Somewhat trust',
        'Strongly trust'
      ],
      constants.Randomize.ALL);
  trust.setPlaceholder('How much do you trust [URL]?');
  return trust;
};

/**
 * Rank how much the attributes describe the page.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createAttributesQuestion = function() {
  var attributes = new ScaleQuestion(
      constants.QuestionType.MULT_HORIZ_SCALE,
      'To what degree do each of the following adjectives describe this page?',
      true,
      [
        'Not at all',
        'A little bit',
        'A moderate amount',
        'Very much',
        'A great deal'
      ],
      constants.Randomize.ALL);
  attributes.setAttributes(
      [
        'annoying',
        'comforting',
        'scary',
        'helpful',
        'confusing',
        'informative'
      ]);
  return attributes;
};

/**
 * Rank how much the attributes describe the dialog.
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogAttributesQuestion = function() {
  var attributes = new ScaleQuestion(
      constants.QuestionType.MULT_HORIZ_SCALE,
      'To what degree do each of the following adjectives describe the dialog?',
      true,
      [
        'Not at all',
        'A little bit',
        'A moderate amount',
        'Very much',
        'A great deal'
      ],
      constants.Randomize.ALL);
  attributes.setAttributes(
      [
        'annoying',
        'comforting',
        'scary',
        'helpful',
        'confusing',
        'informative'
      ]);
  return attributes;
};

/**
 * Record the URL?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createRecordUrlQuestion = function() {
  var url = new FixedQuestion(
      constants.QuestionType.RADIO,
      'May we record the URL of the website you were trying to visit, ' +
          surveyDriver.questionUrl + ', with your responses?',
      true,
      ['Yes', 'No'],
      constants.Randomize.NONE);
  url.setPlaceholder(
      'May we record the URL of the website you were trying to visit, ' +
      '[URL], with your responses?');
  return url;
};

/**
 * What is the name of the extension or app you were trying to install?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createExtensionNameQuestion = function() {
  var extensionName = new EssayQuestion(
      constants.QuestionType.SHORT_STRING,
      'What is the name of the extension or app you were trying to install?',
      false);
  return extensionName;
}

/**
 * Anything else?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createClarificationQuestion = function() {
  var extra = new EssayQuestion(
      constants.QuestionType.SHORT_ESSAY,
      'Please use this space to clarify any of your responses from above or ' +
          'let us know how we can improve this survey.',
      false);
  return extra;
};
<commit_msg>fixed<commit_after>/**
 * Defines common questions that are repeated across surveys.
 */

var commonQuestions = {};

/**
 * What website were you trying to visit when the page was displayed?
 * @returns {object} The DOM subtree with the question.
*/
commonQuestions.createWhatWebsiteQuestion = function() {
  var whatWebsite = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What website were you trying to visit when the page was displayed?',
      true);
  return whatWebsite;
};

/**
 * How did you choose between the two options? Note that the options should be
 * flipped for the proceed & no-proceed scenarios.
 * @param {string} The option that the user chose.
 * @param {string} The option that the user didn't choose.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createChoiceQuestion = function(chosen, alternative) {
  var howChoose = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'You chose "' + chosen + '" instead of "' + alternative + '."' +
          ' How did you choose between the two options?',
      true);
  howChoose.setPlaceholder('You chose "' + chosen + '" instead of "[URL]."' +
      ' How did you choose between the two options?');
  return howChoose;
};

/**
 * Why did you choose not to proceed to <website>?
 * @param {string} The option that the user may have chosen,
 *     without enclosing quotes.
 * @returns {object} The DOM subtree with the question.
*/
commonQuestions.createNotProceedChoiceQuestion = function(chosen) {
  var howChoose = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'You chose the "' + chosen + '" option, or you closed the page.' +
      ' Why did you choose not to proceed to ' + surveyDriver.questionUrl +
      '?', true);
  howChoose.setPlaceholder(
      'You chose the "' + chosen + '" option, or you closed the page.' +
      ' Why did you choose not to proceed to [URL]?');
  return howChoose;
};

/**
 * What was the page trying to tell you?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createPageMeaningQuestion = function() {
  var meaning = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What was the page trying to tell you, in your own words?',
      true);
  return meaning;
};

/**
 * What was the dialog trying to tell you?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogMeaningQuestion = function() {
  var meaning = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What was the dialog trying to tell you, in your own words?',
      true);
  return meaning;
};

/**
 * Who do you think the page was from?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createPageSourceQuestion = function() {
  var source = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Who was the page from?',
      true,
      [
        'Chrome (my browser)',
        'A hacker',
        prettyPrintOS(),
        surveyDriver.questionUrl,
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return source;
};

/**
 * Who do you think the dialog was from?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogSourceQuestion = function() {
  var source = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Who was the dialog from?',
      true,
      [
        'Chrome (my browser)',
        'A hacker',
        prettyPrintOS(),
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return source;
};

/**
 * Have you visited example.com before?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createHistoryQuestions = function() {
  var hist = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Have you visited ' + surveyDriver.questionUrl + ' before?',
      true,
      ['Yes', 'No', 'I don\'t know'],
      constants.Randomize.ANCHOR_LAST);
  hist.setPlaceholder('Have you visited [URL] before?');
  var errorBefore = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Have you seen a page like the one pictured above when trying to visit ' +
          surveyDriver.questionUrl + ' before?',
      true,
      ['Yes', 'No', 'I don\'t know'],
      constants.Randomize.ANCHOR_LAST);
  errorBefore.setPlaceholder(
      'Have you seen a page like the one pictured above when trying to visit ' +
      '[URL] before?');
  hist.addDependentQuestion(errorBefore, 'Yes');
  return hist;
};

/**
 * What led you to the page?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createReferrerQuestion = function() {
  var referrer = new FixedQuestion(
      constants.QuestionType.RADIO,
      'What led you to the page?',
      true,
      [
        'Entered or typed a URL',
        'Used a search engine',
        'Clicked link from an email message',
        'Clicked link in a chat window',
        'Clicked link on a web page',
        constants.OTHER
      ],
      constants.Randomize.ANCHOR_LAST);
  return referrer;
};

/**
 * What led you to install the extension or app mentioned in the dialog?
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogReferrerQuestion = function() {
  var referrer = new EssayQuestion(
      constants.QuestionType.LONG_ESSAY,
      'What led you to try to install the extension or app mentioned' +
      ' in the dialog?',
      true);
  return referrer;
};

/**
 * Do you have an account on example.com?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createAccountQuestion = function() {
  var account = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Do you have an account on ' + surveyDriver.questionUrl + '?',
      true,
      ['Yes', 'No', 'I\'m not sure', 'I prefer not to answer'],
      constants.Randomize.ANCHOR_LAST);
  account.setPlaceholder('Do you have an account on [URL]?');
  return account;
};

/**
 * Were you trying to visit example.com when you saw the page instead?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createVisitQuestion = function() {
  var visit = new FixedQuestion(
      constants.QuestionType.RADIO,
      'Were you trying to visit ' + surveyDriver.questionUrl +
           ' when you saw the page instead?',
      true,
      ['Yes', 'No', 'I\'m not sure', 'I prefer not to answer'],
      constants.Randomize.ANCHOR_LAST);
  visit.setPlaceholder('Were you trying to visit [URL] when you saw the page ' +
      'instead?');
  return visit;
};

/**
 * How much do you trust example.com?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createTrustQuestion = function() {
  var trust = new ScaleQuestion(
      constants.QuestionType.VERTICAL_SCALE,
      'How much do you trust ' + surveyDriver.questionUrl + '?',
      true,
      [
        'Strongly distrust',
        'Somewhat distrust',
        'Neither trust nor distrust',
        'Somewhat trust',
        'Strongly trust'
      ],
      constants.Randomize.ALL);
  trust.setPlaceholder('How much do you trust [URL]?');
  return trust;
};

/**
 * Rank how much the attributes describe the page.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createAttributesQuestion = function() {
  var attributes = new ScaleQuestion(
      constants.QuestionType.MULT_HORIZ_SCALE,
      'To what degree do each of the following adjectives describe this page?',
      true,
      [
        'Not at all',
        'A little bit',
        'A moderate amount',
        'Very much',
        'A great deal'
      ],
      constants.Randomize.ALL);
  attributes.setAttributes(
      [
        'annoying',
        'comforting',
        'scary',
        'helpful',
        'confusing',
        'informative'
      ]);
  return attributes;
};

/**
 * Rank how much the attributes describe the dialog.
 * This variant is for extension/app install dialogs.
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createDialogAttributesQuestion = function() {
  var attributes = new ScaleQuestion(
      constants.QuestionType.MULT_HORIZ_SCALE,
      'To what degree do each of the following adjectives describe the dialog?',
      true,
      [
        'Not at all',
        'A little bit',
        'A moderate amount',
        'Very much',
        'A great deal'
      ],
      constants.Randomize.ALL);
  attributes.setAttributes(
      [
        'annoying',
        'comforting',
        'scary',
        'helpful',
        'confusing',
        'informative'
      ]);
  return attributes;
};

/**
 * Record the URL?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createRecordUrlQuestion = function() {
  var url = new FixedQuestion(
      constants.QuestionType.RADIO,
      'May we record the URL of the website you were trying to visit, ' +
          surveyDriver.questionUrl + ', with your responses?',
      true,
      ['Yes', 'No'],
      constants.Randomize.NONE);
  url.setPlaceholder(
      'May we record the URL of the website you were trying to visit, ' +
      '[URL], with your responses?');
  return url;
};

/**
 * What is the name of the extension or app you were trying to install?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createExtensionNameQuestion = function() {
  var extensionName = new EssayQuestion(
      constants.QuestionType.SHORT_STRING,
      'What is the name of the extension or app you were trying to install?',
      false);
  return extensionName;
}

/**
 * Anything else?
 * @returns {object} The DOM subtree with the question.
 */
commonQuestions.createClarificationQuestion = function() {
  var extra = new EssayQuestion(
      constants.QuestionType.SHORT_ESSAY,
      'Please use this space to clarify any of your responses from above or ' +
          'let us know how we can improve this survey.',
      false);
  return extra;
};
<|endoftext|>


<commit_before>var height = $('#main-bar').height()

$('#side-bar').height(height)
$('#icon-bar').height(height)

$(window).on("load", function () {
    $(window).scroll(function () {
        var windowBottom = $(this).scrollTop() + $(this).innerHeight();
        $(".fade").each(function () {
            /* Check the location of each desired element */
            var objectBottom = $(this).offset().top + $(this).outerHeight();

            /* If the element is completely within bounds of the window, fade it in */
            if (objectBottom < windowBottom) { //object comes into view (scrolling down)
                if ($(this).css("opacity") == 0) {
                    $(this).fadeTo(500, 1);
                }
            } else { //object goes out of view (scrolling up)
                if ($(this).css("opacity") == 1) {
                    $(this).fadeTo(500, 0);
                }
            }
        });
    }).scroll(); //invoke scroll-handler on page-load
});

$('#icon-bar a').attr('target', '_blank');

$('.persianstat'
<commit_msg>nothing<commit_after>var height = $('#main-bar').height()

$('#side-bar').height(height)
$('#icon-bar').height(height)

$(window).on("load", function () {
    $(window).scroll(function () {
        var windowBottom = $(this).scrollTop() + $(this).innerHeight();
        $(".fade").each(function () {
            /* Check the location of each desired element */
            var objectBottom = $(this).offset().top + $(this).outerHeight();

            /* If the element is completely within bounds of the window, fade it in */
            if (objectBottom < windowBottom) { //object comes into view (scrolling down)
                if ($(this).css("opacity") == 0) {
                    $(this).fadeTo(500, 1);
                }
            } else { //object goes out of view (scrolling up)
                if ($(this).css("opacity") == 1) {
                    $(this).fadeTo(500, 0);
                }
            }
        });
    }).scroll(); //invoke scroll-handler on page-load
});

$('#icon-bar a').attr('target', '_blank');
$('.persianstat img').hide();
<|endoftext|>