<commit_before>/**
* timbles.js
* definitely the most lightweight jquery table plugin ever
* except probably not
*
* @version 1.1.0
* @author jenn schiffer http://jennmoney.biz
*/

( function( $ ) {

'use strict';

var pluginName = 'timbles';

var defaults = {
  sortKey: null,
  sortOrder: null
};

var classes = {
  disabled: 'disabled',
  active: 'active',
  headerRow: 'header-row',
  label: 'label',
  sortASC: 'sorted-asc',
  sortDESC: 'sorted-desc',
  noSort: 'no-sort',
  paginationToolsContainer: 'pagination',
  paginationNavArrows: 'nav-arrows',
  paginationNavRowCountChoice: 'row-count-choice'
};

var copy = {
  firstPageArrow: ' << ',
  prevPageArrow: ' < ',
  nextPageArrow: ' > ',
  lastPageArrow: ' >> ',
  page: 'page',
  of: 'of'
};

var methods = {

  // Initialize timble, er, i mean table //
  init: function( opts ) {
    return this.each( function() {
      var $this = $( this ).addClass( pluginName );
      var options = $.extend( {}, defaults, opts );
      var data = {
        dataConfig: methods.parseDataConfig( options.dataConfig ),
        sorting: options.sorting,
        pagination: options.pagination
      };
      $this.data( pluginName, data );

      if ( data.dataConfig ) {
        methods.generateTableFromJson.call( $this );
      } else {
        methods.setupExistingTable.call( $this );
      }
    } );
  },

  parseDataConfig: function( dataConfig ) {
    if ( !( dataConfig instanceof Object ) ) {
      return dataConfig;
    } else if ( dataConfig.hasOwnProperty( 'columns' ) ) {
      $.each( dataConfig.columns, function( index, columnConfig ) {
        if ( columnConfig.hasOwnProperty( 'dataFilter' ) &&
             !columnConfig.hasOwnProperty( 'textTransform' ) ) {

          // If a dataFilter is defined (old-style) and no textTransform,
          // use the dataFilter as textTransform
          columnConfig.textTransform = columnConfig.dataFilter;
        } else if ( !columnConfig.hasOwnProperty( 'textTransform' ) ) {

          // Add a do-nothing textTransform if none is provided
          columnConfig.textTransform = function( obj ) { return obj; };
        }
        if ( !columnConfig.hasOwnProperty( 'valueTransform' ) ) {

          // Add a do-nothing valueTransform if none is provided
          columnConfig.valueTransform = function( obj ) { return obj; };
        }
      } );
    }
    return dataConfig;
  },

  setupExistingTable: function() {
    var data = this.data( pluginName );

    // For each header cell, store its column number
    var $headerRow = data.$headerRow = this.find( 'thead tr' ).eq( 0 )
      .addClass( classes.headerRow );
    $headerRow.find( 'th' ).each( function( index ) {
      $( this ).data( 'timbles-column-index', index );
    } );

    // Start enabling any given features
    methods.enableFeaturesSetup.call( this );
  },

  generateTableFromJson: function() {
    var data = this.data( pluginName );
    var $headerRow = data.$headerRow = $( '<tr>' )
      .addClass( classes.headerRow )
      .appendTo( $( '<thead>' ).appendTo( this ) );

    // Generate and add cell headers to header row
    $.each( data.dataConfig.columns, function( index, value ) {
      $( '<th>' )
        .attr( 'id', value.id )
        .addClass( value.noSorting ? classes.noSort : null )
        .data( 'timbles-column-index', index )
        .text( value.label )
        .appendTo( $headerRow );
    } );

    if ( $.isArray( data.dataConfig.data ) ) {

      // No need for ajax call if data is local array
      methods.generateRowsFromData.call( this, data.dataConfig.data, data.dataConfig.columns );

      // Start enabling any given features
      methods.enableFeaturesSetup.call( this );
    } else {

      // Get external json file given
      $.getJSON( data.dataConfig.data, function( json ) {
        methods.generateRowsFromData.call( this, json, data.dataConfig.columns );
      }.bind( this ) ).then( function() {

        // Start enabling any given features
        methods.enableFeaturesSetup.call( this );
      }.bind( this ) );
    }
  },

  generateRowsFromData: function( rowData, columnConfig ) {
    $.each( rowData, function( index, row ) {

      // Add rows to the current table
      var $newRow = $( '<tr>' ).appendTo( this );
      $.each( columnConfig, function( index, column ) {

        // Add cells to the current row
        var cellValue = row[ column.id ];
        $( '<td>' )
          .attr( 'data-value', column.valueTransform( cellValue ) )
          .text( column.textTransform( cellValue ) )
          .appendTo( this );
      }.bind( $newRow ) );
    }.bind( this ) );
  },

  enableFeaturesSetup: function() {
    var data = this.data( pluginName );
    data.tableRows = this.find( 'tbody tr' ).get();

    if ( data.sorting ) {
      methods.enableSorting.call( this );

      if ( data.sorting.keyId ) {
        methods.sortColumn.call( this, data.sorting.keyId, data.sorting.order );
      }
    }

    if ( data.pagination ) {
      methods.enablePagination.call( this, data.pagination.recordsPerPage );
    }
  },

  enableSorting: function() {
    this.find( 'th' ).not( '.no-sort' ).on(
      'click', methods.sortColumnEvent.bind( this ) );
  },

  sortColumn: function( key, order ) {

    // Sort a column identified by its key in a given order
    // If `key` is numeric, it is treated as the column index to sort
    // If `order` is not given, this will do the same as clicking the header
    if ( typeof key === 'number' ) {
      var data = this.data( pluginName );
      data.$headerRow.find( 'th' ).eq( key ).trigger( 'click', order );
    } else {
      this.find( '#' + key ).trigger( 'click', order );
    }
  },

  sortColumnEvent: function( event, order ) {
    var data = this.data( pluginName );

    // Determine order and update header sort classes
    var $sortHeader = $( event.target );
    var sortColumn = $sortHeader.data( 'timbles-column-index' );

    if ( !order ) {
      order = $sortHeader.hasClass( classes.sortASC ) ? 'desc' : 'asc';
    }
    data.$headerRow.find( 'th' )
      .removeClass( classes.sortASC )
      .removeClass( classes.sortDESC );
    $sortHeader.addClass( ( order === 'asc' ) ? classes.sortASC : classes.sortDESC );

    // Determine column values to actually sort by
    var sortMap = $.map( data.tableRows, function( row ) {
      var cell = row.children[ sortColumn ];
      var dataValue = cell.getAttribute( 'data-value' );
      if ( dataValue === null ) {
        dataValue = cell.textContent || cell.innerText;
      } else if ( parseFloat( dataValue ).toString() === dataValue ) {
        dataValue = parseFloat( dataValue );
      }
      return {
        node: row,
        value: dataValue
      };
    } );

    // Sort the mapping by the extract column values
    sortMap.sort( function( a, b ) {
      if ( a.value > b.value ) {
        return ( order === 'asc' ) ? 1 : -1;
      }
      if ( a.value < b.value ) {
        return ( order === 'asc' ) ? -1 : 1;
      }
      return 0;
    } );

    // Create new canonical row set sorted based on sortMap
    data.tableRows = $.map( sortMap, function( row ) {
      return row.node;
    } );

    if ( data.pagination ) {

      // If table was paginated, load content for first page
      methods.goToPage.call( this, 1 );
    } else {

      // If not, add sorted rows in order (on detached body for performance)
      var tableBody = this.find( 'tbody' ).detach().get( 0 );
      $.each( data.tableRows, function() {
        tableBody.appendChild( this );
      } );
      this.append( tableBody );
    }
  },

  enablePagination: function( count ) {
    var data = this.data( pluginName );

    // Without records, abandon pagination. Without pagination config, create empty
    if ( !data.tableRows || data.tableRows.length === 0 ) {
      return;
    } else if ( !data.pagination ) {
      data.pagination = {};
    }

    // Update pagination page size and count
    data.pagination.currentPage = 1;
    data.pagination.recordsPerPage = count;
    data.pagination.lastPage = Math.ceil( data.tableRows.length / count );

    // Create tools if they don't exist yet
    if ( !data.$paginationToolsContainer ) {
      methods.generatePaginationTools.call( this );
    }

    // Actually place records for the first page
    methods.goToPage.call( this, 1 );
  },

  generatePaginationTools: function() {
    var data = this.data( pluginName );

    data.$paginationToolsContainer = $( '<div>' )
      .addClass( classes.paginationToolsContainer )
      .insertAfter( this );

    if ( !data.pagination.nav ) {

      // If no pagination is configured, default to showing arrows only
      methods.generatePaginationNavArrows.call( this );
    } else {
      for ( var navObject in data.pagination.nav ) {
        switch ( navObject ) {
          case 'arrows':
            methods.generatePaginationNavArrows.call( this );
            break;
          case 'rowCountChoice':
            methods.generatePaginationNavRowCountChoice.call( this );
            break;
        }
      }
    }
    methods.updatePaginationTools.call( this );
  },

  generatePaginationNavArrows: function() {
    var data = this.data( pluginName );
    var $navButton = $( '<button>' ).attr( 'type', 'button' );

    data.$linkFirstPage = $navButton.clone().text( copy.firstPageArrow );
    data.$linkPrevPage = $navButton.clone().text( copy.prevPageArrow );
    data.$linkNextPage = $navButton.clone().text( copy.nextPageArrow );
    data.$linkLastPage = $navButton.clone().text( copy.lastPageArrow );
    var $pageNumberTracker = $( '<span>' )
      .addClass( 'page-number-tracker' )
      .text( copy.page + ' ' )
      .append( $( '<span>' ).addClass( 'pointer-this-page' ).text( data.pagination.currentPage ) )
      .append( ' ' + copy.of + ' ' )
      .append( $( '<span>' ).addClass( 'pointer-last-page' ).text( data.pagination.lastPage ) );

    data.$pointerThisPage = $pageNumberTracker.find( '.pointer-this-page' );
    data.$pointerLastPage = $pageNumberTracker.find( '.pointer-last-page' );
    $( '<div>' )
      .addClass( classes.paginationNavArrows )
      .append( data.$linkFirstPage )
      .append( data.$linkPrevPage )
      .append( $pageNumberTracker )
      .append( data.$linkNextPage )
      .append( data.$linkLastPage )
      .appendTo( data.$paginationToolsContainer );

    // Register event listeners
    data.$linkFirstPage.on( 'click', function() {
      methods.goToPage.call( this, 1 );
    }.bind( this ) );

    data.$linkPrevPage.on( 'click', function() {
      if ( data.pagination.currentPage > 1 ) {
        methods.goToPage.call( this, data.pagination.currentPage - 1 );
      }
    }.bind( this ) );

    data.$linkNextPage.on( 'click', function() {
      if ( data.pagination.currentPage < data.pagination.lastPage ) {
        methods.goToPage.call( this, data.pagination.currentPage + 1 );
      }
    }.bind( this ) );

    data.$linkLastPage.on( 'click', function() {
      methods.goToPage.call( this, data.pagination.lastPage );
    }.bind( this ) );
  },

  generatePaginationNavRowCountChoice: function() {
    var pageSizeChangeEvent, pageSizeSelection;
    var data = this.data( pluginName );

    pageSizeSelection = $( '<div>' )
      .addClass( classes.paginationNavRowCountChoice )
      .appendTo( data.$paginationToolsContainer );

    pageSizeChangeEvent = function( event ) {
      var $target = $( event.target ).addClass( classes.active );
      $target.siblings().removeClass( classes.active );
      var pageSize = $target.text();

      if ( pageSize.toLowerCase() === 'all' ) {
        pageSize = data.tableRows.length;
      }

      methods.enablePagination.call( this, parseInt( pageSize ) );
    }.bind( this );

    $.each( data.pagination.nav.rowCountChoice, function() {
      $( '<button>' )
        .attr( 'type', 'button' )
        .on( 'click', pageSizeChangeEvent )
        .text( this )
        .toggleClass( classes.active, data.pagination.recordsPerPage === this )
        .appendTo( pageSizeSelection );
    } );
  },

  updatePaginationTools: function() {
    var data = this.data( pluginName );

    function toggleButtons( buttons, disabled ) {
      $.each( buttons, function() {
        this.toggleClass( classes.disabled, disabled );
        this.attr( 'disabled', disabled );
      } );
    }

    // Set buttons inactive if appropriate
    toggleButtons(
      [ data.$linkFirstPage, data.$linkPrevPage ],
      data.pagination.currentPage === 1 );
    toggleButtons(
      [ data.$linkLastPage, data.$linkNextPage ],
      data.pagination.currentPage === data.pagination.lastPage );

    // Update page number tracker
    data.$pointerThisPage.text( data.pagination.currentPage );
    data.$pointerLastPage.text( data.pagination.lastPage );
  },

  goToPage: function( page ) {
    var data = this.data( pluginName );
    var newFirstRowNum = ( page - 1 ) * data.pagination.recordsPerPage;
    var newLastRowNum = Math.min(
      newFirstRowNum + data.pagination.recordsPerPage,
      data.tableRows.length );

    // Check for valid page number
    if ( page < 1 || page > data.pagination.lastPage ) {
      return;
    }

    // Update page number and display page's rows
    data.pagination.currentPage = page;
    this.find( 'tbody tr' ).remove();
    this.find( 'tbody' ).append(
      data.tableRows.slice( newFirstRowNum, newLastRowNum ) );

    // Update pagination tools
    methods.updatePaginationTools.call( this );
  }
};

// Install timbles jQuery plugin
$.fn[ pluginName ] = function( method ) {
  if ( methods.hasOwnProperty( method ) ) {
    var methodArgs = Array.prototype.slice.call( arguments, 1 );
    return methods[ method ].apply( this, methodArgs );
  }
  if ( typeof method === 'object' || !method ) {
    return methods.init.apply( this, arguments );
  }
  $.error( 'The method ' + method + ' literally does not exist. Good job.' );
};

} )( jQuery );
<commit_msg>Added option for 'classes' and 'copy' to be provided as options.<commit_after>/**
* timbles.js
* definitely the most lightweight jquery table plugin ever
* except probably not
*
* @version 1.1.0
* @author jenn schiffer http://jennmoney.biz
*/

( function( $ ) {

'use strict';

var pluginName = 'timbles';

var defaults = {
  sortKey: null,
  sortOrder: null
};

var classes = {
  disabled: 'disabled',
  active: 'active',
  headerRow: 'header-row',
  label: 'label',
  sortASC: 'sorted-asc',
  sortDESC: 'sorted-desc',
  noSort: 'no-sort',
  paginationToolsContainer: 'pagination',
  paginationNavArrows: 'nav-arrows',
  paginationNavRowCountChoice: 'row-count-choice'
};

var copy = {
  firstPageArrow: ' << ',
  prevPageArrow: ' < ',
  nextPageArrow: ' > ',
  lastPageArrow: ' >> ',
  page: 'page',
  of: 'of'
};

var methods = {

  // Initialize timble, er, i mean table //
  init: function( opts ) {
    return this.each( function() {
      var $this = $( this ).addClass( pluginName );
      var options = $.extend( {}, defaults, opts );
      var data = {
        classes: $.extend( {}, classes, options.classes ),
        copy: $.extend( {}, copy, options.copy ),
        dataConfig: methods.parseDataConfig( options.dataConfig ),
        sorting: options.sorting,
        pagination: options.pagination
      };
      $this.data( pluginName, data );

      if ( data.dataConfig ) {
        methods.generateTableFromJson.call( $this );
      } else {
        methods.setupExistingTable.call( $this );
      }
    } );
  },

  parseDataConfig: function( dataConfig ) {
    if ( !( dataConfig instanceof Object ) ) {
      return dataConfig;
    } else if ( dataConfig.hasOwnProperty( 'columns' ) ) {
      $.each( dataConfig.columns, function( index, columnConfig ) {
        if ( columnConfig.hasOwnProperty( 'dataFilter' ) &&
             !columnConfig.hasOwnProperty( 'textTransform' ) ) {

          // If a dataFilter is defined (old-style) and no textTransform,
          // use the dataFilter as textTransform
          columnConfig.textTransform = columnConfig.dataFilter;
        } else if ( !columnConfig.hasOwnProperty( 'textTransform' ) ) {

          // Add a do-nothing textTransform if none is provided
          columnConfig.textTransform = function( obj ) { return obj; };
        }
        if ( !columnConfig.hasOwnProperty( 'valueTransform' ) ) {

          // Add a do-nothing valueTransform if none is provided
          columnConfig.valueTransform = function( obj ) { return obj; };
        }
      } );
    }
    return dataConfig;
  },

  setupExistingTable: function() {
    var data = this.data( pluginName );

    // For each header cell, store its column number
    var $headerRow = data.$headerRow = this.find( 'thead tr' ).eq( 0 )
      .addClass( data.classes.headerRow );
    $headerRow.find( 'th' ).each( function( index ) {
      $( this ).data( 'timbles-column-index', index );
    } );

    // Start enabling any given features
    methods.enableFeaturesSetup.call( this );
  },

  generateTableFromJson: function() {
    var data = this.data( pluginName );
    var $headerRow = data.$headerRow = $( '<tr>' )
      .addClass( data.classes.headerRow )
      .appendTo( $( '<thead>' ).appendTo( this ) );

    // Generate and add cell headers to header row
    $.each( data.dataConfig.columns, function( index, value ) {
      $( '<th>' )
        .attr( 'id', value.id )
        .addClass( value.noSorting ? data.classes.noSort : null )
        .data( 'timbles-column-index', index )
        .text( value.label )
        .appendTo( $headerRow );
    } );

    if ( $.isArray( data.dataConfig.data ) ) {

      // No need for ajax call if data is local array
      methods.generateRowsFromData.call( this, data.dataConfig.data, data.dataConfig.columns );

      // Start enabling any given features
      methods.enableFeaturesSetup.call( this );
    } else {

      // Get external json file given
      $.getJSON( data.dataConfig.data, function( json ) {
        methods.generateRowsFromData.call( this, json, data.dataConfig.columns );
      }.bind( this ) ).then( function() {

        // Start enabling any given features
        methods.enableFeaturesSetup.call( this );
      }.bind( this ) );
    }
  },

  generateRowsFromData: function( rowData, columnConfig ) {
    $.each( rowData, function( index, row ) {

      // Add rows to the current table
      var $newRow = $( '<tr>' ).appendTo( this );
      $.each( columnConfig, function( index, column ) {

        // Add cells to the current row
        var cellValue = row[ column.id ];
        $( '<td>' )
          .attr( 'data-value', column.valueTransform( cellValue ) )
          .text( column.textTransform( cellValue ) )
          .appendTo( this );
      }.bind( $newRow ) );
    }.bind( this ) );
  },

  enableFeaturesSetup: function() {
    var data = this.data( pluginName );
    data.tableRows = this.find( 'tbody tr' ).get();

    if ( data.sorting ) {
      methods.enableSorting.call( this );

      if ( data.sorting.keyId ) {
        methods.sortColumn.call( this, data.sorting.keyId, data.sorting.order );
      }
    }

    if ( data.pagination ) {
      methods.enablePagination.call( this, data.pagination.recordsPerPage );
    }
  },

  enableSorting: function() {
    this.find( 'th' ).not( '.no-sort' ).on(
      'click', methods.sortColumnEvent.bind( this ) );
  },

  sortColumn: function( key, order ) {

    // Sort a column identified by its key in a given order
    // If `key` is numeric, it is treated as the column index to sort
    // If `order` is not given, this will do the same as clicking the header
    if ( typeof key === 'number' ) {
      var data = this.data( pluginName );
      data.$headerRow.find( 'th' ).eq( key ).trigger( 'click', order );
    } else {
      this.find( '#' + key ).trigger( 'click', order );
    }
  },

  sortColumnEvent: function( event, order ) {
    var data = this.data( pluginName );

    // Determine order and update header sort classes
    var $sortHeader = $( event.target );
    var sortColumn = $sortHeader.data( 'timbles-column-index' );

    if ( !order ) {
      order = $sortHeader.hasClass( data.classes.sortASC ) ? 'desc' : 'asc';
    }
    data.$headerRow.find( 'th' )
      .removeClass( data.classes.sortASC )
      .removeClass( data.classes.sortDESC );
    $sortHeader.addClass( ( order === 'asc' ) ? data.classes.sortASC : data.classes.sortDESC );

    // Determine column values to actually sort by
    var sortMap = $.map( data.tableRows, function( row ) {
      var cell = row.children[ sortColumn ];
      var dataValue = cell.getAttribute( 'data-value' );
      if ( dataValue === null ) {
        dataValue = cell.textContent || cell.innerText;
      } else if ( parseFloat( dataValue ).toString() === dataValue ) {
        dataValue = parseFloat( dataValue );
      }
      return {
        node: row,
        value: dataValue
      };
    } );

    // Sort the mapping by the extract column values
    sortMap.sort( function( a, b ) {
      if ( a.value > b.value ) {
        return ( order === 'asc' ) ? 1 : -1;
      }
      if ( a.value < b.value ) {
        return ( order === 'asc' ) ? -1 : 1;
      }
      return 0;
    } );

    // Create new canonical row set sorted based on sortMap
    data.tableRows = $.map( sortMap, function( row ) {
      return row.node;
    } );

    if ( data.pagination ) {

      // If table was paginated, load content for first page
      methods.goToPage.call( this, 1 );
    } else {

      // If not, add sorted rows in order (on detached body for performance)
      var tableBody = this.find( 'tbody' ).detach().get( 0 );
      $.each( data.tableRows, function() {
        tableBody.appendChild( this );
      } );
      this.append( tableBody );
    }
  },

  enablePagination: function( count ) {
    var data = this.data( pluginName );

    // Without records, abandon pagination. Without pagination config, create empty
    if ( !data.tableRows || data.tableRows.length === 0 ) {
      return;
    } else if ( !data.pagination ) {
      data.pagination = {};
    }

    // Update pagination page size and count
    data.pagination.currentPage = 1;
    data.pagination.recordsPerPage = count;
    data.pagination.lastPage = Math.ceil( data.tableRows.length / count );

    // Create tools if they don't exist yet
    if ( !data.$paginationToolsContainer ) {
      methods.generatePaginationTools.call( this );
    }

    // Actually place records for the first page
    methods.goToPage.call( this, 1 );
  },

  generatePaginationTools: function() {
    var data = this.data( pluginName );

    data.$paginationToolsContainer = $( '<div>' )
      .addClass( data.classes.paginationToolsContainer )
      .insertAfter( this );

    if ( !data.pagination.nav ) {

      // If no pagination is configured, default to showing arrows only
      methods.generatePaginationNavArrows.call( this );
    } else {
      for ( var navObject in data.pagination.nav ) {
        switch ( navObject ) {
          case 'arrows':
            methods.generatePaginationNavArrows.call( this );
            break;
          case 'rowCountChoice':
            methods.generatePaginationNavRowCountChoice.call( this );
            break;
        }
      }
    }
    methods.updatePaginationTools.call( this );
  },

  generatePaginationNavArrows: function() {
    var data = this.data( pluginName );
    var $navButton = $( '<button>' ).attr( 'type', 'button' );

    data.$linkFirstPage = $navButton.clone().text( data.copy.firstPageArrow );
    data.$linkPrevPage = $navButton.clone().text( data.copy.prevPageArrow );
    data.$linkNextPage = $navButton.clone().text( data.copy.nextPageArrow );
    data.$linkLastPage = $navButton.clone().text( data.copy.lastPageArrow );
    var $pageNumberTracker = $( '<span>' )
      .addClass( 'page-number-tracker' )
      .text( data.copy.page + ' ' )
      .append( $( '<span>' ).addClass( 'pointer-this-page' ).text( data.pagination.currentPage ) )
      .append( ' ' + data.copy.of + ' ' )
      .append( $( '<span>' ).addClass( 'pointer-last-page' ).text( data.pagination.lastPage ) );

    data.$pointerThisPage = $pageNumberTracker.find( '.pointer-this-page' );
    data.$pointerLastPage = $pageNumberTracker.find( '.pointer-last-page' );
    $( '<div>' )
      .addClass( data.classes.paginationNavArrows )
      .append( data.$linkFirstPage )
      .append( data.$linkPrevPage )
      .append( $pageNumberTracker )
      .append( data.$linkNextPage )
      .append( data.$linkLastPage )
      .appendTo( data.$paginationToolsContainer );

    // Register event listeners
    data.$linkFirstPage.on( 'click', function() {
      methods.goToPage.call( this, 1 );
    }.bind( this ) );

    data.$linkPrevPage.on( 'click', function() {
      if ( data.pagination.currentPage > 1 ) {
        methods.goToPage.call( this, data.pagination.currentPage - 1 );
      }
    }.bind( this ) );

    data.$linkNextPage.on( 'click', function() {
      if ( data.pagination.currentPage < data.pagination.lastPage ) {
        methods.goToPage.call( this, data.pagination.currentPage + 1 );
      }
    }.bind( this ) );

    data.$linkLastPage.on( 'click', function() {
      methods.goToPage.call( this, data.pagination.lastPage );
    }.bind( this ) );
  },

  generatePaginationNavRowCountChoice: function() {
    var pageSizeChangeEvent, pageSizeSelection;
    var data = this.data( pluginName );

    pageSizeSelection = $( '<div>' )
      .addClass( data.classes.paginationNavRowCountChoice )
      .appendTo( data.$paginationToolsContainer );

    pageSizeChangeEvent = function( event ) {
      var $target = $( event.target ).addClass( data.classes.active );
      $target.siblings().removeClass( data.classes.active );
      var pageSize = $target.text();

      if ( pageSize.toLowerCase() === 'all' ) {
        pageSize = data.tableRows.length;
      }

      methods.enablePagination.call( this, parseInt( pageSize ) );
    }.bind( this );

    $.each( data.pagination.nav.rowCountChoice, function() {
      $( '<button>' )
        .attr( 'type', 'button' )
        .on( 'click', pageSizeChangeEvent )
        .text( this )
        .toggleClass( data.classes.active, data.pagination.recordsPerPage === this )
        .appendTo( pageSizeSelection );
    } );
  },

  updatePaginationTools: function() {
    var data = this.data( pluginName );

    function toggleButtons( buttons, disabled ) {
      $.each( buttons, function() {
        this.toggleClass( data.classes.disabled, disabled );
        this.attr( 'disabled', disabled );
      } );
    }

    // Set buttons inactive if appropriate
    toggleButtons(
      [ data.$linkFirstPage, data.$linkPrevPage ],
      data.pagination.currentPage === 1 );
    toggleButtons(
      [ data.$linkLastPage, data.$linkNextPage ],
      data.pagination.currentPage === data.pagination.lastPage );

    // Update page number tracker
    data.$pointerThisPage.text( data.pagination.currentPage );
    data.$pointerLastPage.text( data.pagination.lastPage );
  },

  goToPage: function( page ) {
    var data = this.data( pluginName );
    var newFirstRowNum = ( page - 1 ) * data.pagination.recordsPerPage;
    var newLastRowNum = Math.min(
      newFirstRowNum + data.pagination.recordsPerPage,
      data.tableRows.length );

    // Check for valid page number
    if ( page < 1 || page > data.pagination.lastPage ) {
      return;
    }

    // Update page number and display page's rows
    data.pagination.currentPage = page;
    this.find( 'tbody tr' ).remove();
    this.find( 'tbody' ).append(
      data.tableRows.slice( newFirstRowNum, newLastRowNum ) );

    // Update pagination tools
    methods.updatePaginationTools.call( this );
  }
};

// Install timbles jQuery plugin
$.fn[ pluginName ] = function( method ) {
  if ( methods.hasOwnProperty( method ) ) {
    var methodArgs = Array.prototype.slice.call( arguments, 1 );
    return methods[ method ].apply( this, methodArgs );
  }
  if ( typeof method === 'object' || !method ) {
    return methods.init.apply( this, arguments );
  }
  $.error( 'The method ' + method + ' literally does not exist. Good job.' );
};

} )( jQuery );
<|endoftext|>


<commit_before>(function() {
    var options;
    var current = {};

    var trackInfo = function(context) {
        var player = $(context);
        var title;
        if ($(context).parent().hasClass('playlist-player')) {
            title =  $('.tracks li.playing .info a').text().trim();
        }

        if(!title)
            title = $('.soundTitle__title', player).text().trim();

        if(!title)
            title = $('.info-header h3 a, .info-header h1 em', player).text().trim();

        return title.split(" - ");
    };

    var duration = function(context) {
        var node = $('span.duration', context);
        if (node.length)
            return parseInt($(node).attr('title').substring(2), 10);

        node = $('.timeIndicator__total', context);
        if (node.length) {
            // time is given as h.m.s
            var digits = node[0].innerHTML.split(/\D/),
                seconds = 0, length, digit;

            length = digits.length;
            if (length > 3)
                return;

            while (digits.length) {
                d = digits.pop();
                seconds += parseInt(d, 10) * Math.pow(60, length - digits.length - 1);
            }
            return seconds;
        }
    };

    var song = function(context) {
        var track = trackInfo(context);
        return {
                'artist': track[0],
                'track': track[1],
                'duration': duration(context)
        };
    };

    var container = function(context) {
        var parent;
        parent = $(context).closest('li.set')[0] ||  // 'old' soundcloud
                 $(context).closest('div.player')[0] ||
                 $(context).closest('div.sound.playing')[0];  // 'new' ...

        if (!parent) {
            // Couldn't find container element for this song...
        }

        return parent;
    };

    $(document).ready(function() {
        var selectors = 'div.timeIndicator__current, .timecodes .editable';

        $(selectors).live('DOMSubtreeModified', function() {
            var next = $(this).hasClass('timeIndicator__current');

            var parent = $(this).closest('.playing');
            if (!parent.length)
                return;

            if (!current || !current.context || current.context[0] != parent[0]) {
                current = {submitted: false};
                current.context = parent;
            }

            if (!current.player) {
                current.player = container(current.context);
                return;
            }

            if (current.validating)
                return;

            if (next && !parent.hasClass('endOfSound'))
                return;

            var playhead = $('.playhead[style]', parent);
            if (!next && parseFloat(playhead.attr('style').substring(6)) < 70)
                return;

            current.validating = true;

            var s = song(current.player);
            chrome.extension.sendRequest({type: 'validate',
                                        artist: s.artist,
                                         track: s.track},
            function(response) {
                current.validated = response;
                if (response !== false) {
                    chrome.extension.sendRequest({type: 'nowPlaying',
                                                artist: response.artist,
                                                 track: response.track,
                                              duration: s.duration});
                }

                else {
                    chrome.extension.sendRequest({type: 'nowPlaying',
                                              duration: s.duration});
                }
            });
        });
    });

    $(window).unload(function() {
        chrome.extension.sendRequest({type: 'reset'});
        return true;
    });
})();
<commit_msg>use username if track title is not in "artist - track" format<commit_after>(function() {
    var options;
    var current = {};

    var trackInfo = function(context) {
        var player = $(context);
        var title;

        if ($(context).parent().hasClass('playlist-player'))
            title =  $('.tracks li.playing .info a').text().trim();

        if(!title)
            title = $('.soundTitle__title', player).text().trim();

        if(!title)
            title = $('.info-header h3 a, .info-header h1 em', player).text().trim();

        trackInfo = title.split(" - ");

        if(!trackInfo[1])
            return [artist(context),title];

        return trackInfo;
    };

    var artist = function(context) {
        var node = $('.info-header .user-name', context);
        if (node.length)
            return node[0].text;

        node = $('.soundTitle__username', context);
        if (node.length)
            return node[0].text;
    };

    var duration = function(context) {
        var node = $('span.duration', context);
        if (node.length)
            return parseInt($(node).attr('title').substring(2), 10);

        node = $('.timeIndicator__total', context);
        if (node.length) {
            // time is given as h.m.s
            var digits = node[0].innerHTML.split(/\D/),
                seconds = 0, length, digit;

            length = digits.length;
            if (length > 3)
                return;

            while (digits.length) {
                d = digits.pop();
                seconds += parseInt(d, 10) * Math.pow(60, length - digits.length - 1);
            }
            return seconds;
        }
    };

    var song = function(context) {
        var track = trackInfo(context);
        return {
                'artist': track[0],
                'track': track[1],
                'duration': duration(context)
        };
    };

    var container = function(context) {
        var parent;
        parent = $(context).closest('li.set')[0] ||  // 'old' soundcloud
                 $(context).closest('div.player')[0] ||
                 $(context).closest('div.sound.playing')[0];  // 'new' ...

        if (!parent) {
            // Couldn't find container element for this song...
        }

        return parent;
    };

    $(document).ready(function() {
        var selectors = 'div.timeIndicator__current, .timecodes .editable';

        $(selectors).live('DOMSubtreeModified', function() {
            var next = $(this).hasClass('timeIndicator__current');

            var parent = $(this).closest('.playing');
            if (!parent.length)
                return;

            if (!current || !current.context || current.context[0] != parent[0]) {
                current = {submitted: false};
                current.context = parent;
            }

            if (!current.player) {
                current.player = container(current.context);
                return;
            }

            if (current.validating)
                return;

            if (next && !parent.hasClass('endOfSound'))
                return;

            var playhead = $('.playhead[style]', parent);
            if (!next && parseFloat(playhead.attr('style').substring(6)) < 70)
                return;

            current.validating = true;

            var s = song(current.player);
            chrome.extension.sendRequest({type: 'validate',
                                        artist: s.artist,
                                         track: s.track},
            function(response) {
                current.validated = response;
                if (response !== false) {
                    chrome.extension.sendRequest({type: 'nowPlaying',
                                                artist: response.artist,
                                                 track: response.track,
                                              duration: s.duration});
                }

                else {
                    chrome.extension.sendRequest({type: 'nowPlaying',
                                              duration: s.duration});
                }
            });
        });
    });

    $(window).unload(function() {
        chrome.extension.sendRequest({type: 'reset'});
        return true;
    });
})();
<|endoftext|>


<commit_before>enyo.kind({
	name: "App",
	kind: "FittableRows",
	fit: true,
  tokens: [],
	components:[
    {kind: WebLetter.Config, name: "config"},
		{kind: "onyx.Toolbar", content: "WebLetter"},
		{kind: "enyo.Scroller", fit: true,
      components: [
			{name: "main", classes: "nice-padding", allowHtml: true, components: [
        {kind: "onyx.Groupbox", name: "settings", components: [
          {kind: "WebLetter.BooleanToken", key: "folding", description: "Folding Marks"},
          {kind: "WebLetter.BooleanToken", key: "footer", description: "Footer"},
          {kind: "WebLetter.BooleanToken", key: "logo", description: "Logo"}
          ]
        },
        {kind: "WebLetter.AddressToken", name: "addresser", description: "Addresser", key: "from"},
        {kind: "WebLetter.AddressToken", name: "addressee", description: "Addressee", key: "to"},
        {kind: "onyx.Groupbox", name: "contentGroup", components: [
          {kind: "WebLetter.TextToken", description: "Subject", key: "subject", isOptional: true},
          {kind: "WebLetter.TextToken", description: "Opening", key: "opening"},
          {kind: "WebLetter.TextAreaToken", name: "lettercontent", allowHtml: true, key: "content", description: "Content", inputWidth: "100%", inputHeight: "400px"},
          {kind: "WebLetter.TextToken", key: "closing", description: "Closing"},
          {kind: "WebLetter.TextToken", key: "signatureaddition", description: "Signature Addition", isOptional: true}
          ]
        },
        {tag: "form", name: "pdfform", showing: false},
          {kind: "onyx.Button", name: "go", content: "Generate PDF", ontap: "go"}
        ]
      }
		]}
	],
  create: function() {
    this.tokens = new Array();
    this.inherited(arguments);
    // set up form
    this.$.pdfform.attributes.method=this.$.config.getMethod();
    this.$.pdfform.attributes.action=this.$.config.getServerUrl();
  },
  handlers: {
    onNewToken: "registerToken",
    onDisableToken: "deregisterToken"
  },
  registerToken: function(inSender, inEvent) {
    this.tokens.push(inEvent.originator);
    return true;
  },
  deregisterToken: function(inSender, inEvent) {
    // do nothing since disabled tokens might be optional and
    // need the %nopt-token to be replaced
    return true;
  },
  go: function() {
    //this.$.mylogger.addContent(this.$.lettercontent.getValue());
    this.$.pdfform.destroyComponents();
    for (i in this.tokens) {
      if (this.tokens[i].isEnabled) {
        // add opt-token if the token is optional
        if (this.tokens[i].isOptional) {
          var key = "%opt-" + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: "input", attributes: {name: key, value: ""}});
        }
        if (this.tokens[i].topToken) {
          var type;
          var content = "";
          var value = "";
          if (this.tokens[i].multiline) {
            type = "textarea";
            content = this.tokens[i].getInput();
          } else {
            type = "input";
            value = this.tokens[i].getInput();
          }
          var keyprefix = "token-";
          var key = keyprefix + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: type, attributes: {name: key, value: value}, content: content});
        }
      }
      else { // !this.tokens[i].isEnabled
        // only add the nopt token
        if (this.tokens[i].isOptional) {
          var key = "%nopt-" + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: "input", attributes: {name: key, value: ""}});
        }
      }
    }
    this.$.pdfform.render();
    if (this.$.config.getDebugging()) {
      var components = this.$.pdfform.getComponents();
      for (i in components) {
        var attrs = components[i].attributes;
        enyo.log("'" + attrs.name + "': '" + attrs.value + "'");
      }
    }
    else {
      document.getElementById(this.$.pdfform.getAttribute("id")).submit();
    }
  }

});
<commit_msg>main app: set signatureaddition to disabled by default<commit_after>enyo.kind({
	name: "App",
	kind: "FittableRows",
	fit: true,
  tokens: [],
	components:[
    {kind: WebLetter.Config, name: "config"},
		{kind: "onyx.Toolbar", content: "WebLetter"},
		{kind: "enyo.Scroller", fit: true,
      components: [
			{name: "main", classes: "nice-padding", allowHtml: true, components: [
        {kind: "onyx.Groupbox", name: "settings", components: [
          {kind: "WebLetter.BooleanToken", key: "folding", description: "Folding Marks"},
          {kind: "WebLetter.BooleanToken", key: "footer", description: "Footer"},
          {kind: "WebLetter.BooleanToken", key: "logo", description: "Logo"}
          ]
        },
        {kind: "WebLetter.AddressToken", name: "addresser", description: "Addresser", key: "from"},
        {kind: "WebLetter.AddressToken", name: "addressee", description: "Addressee", key: "to"},
        {kind: "onyx.Groupbox", name: "contentGroup", components: [
          {kind: "WebLetter.TextToken", description: "Subject", key: "subject", isOptional: true},
          {kind: "WebLetter.TextToken", description: "Opening", key: "opening"},
          {kind: "WebLetter.TextAreaToken", name: "lettercontent", allowHtml: true, key: "content", description: "Content", inputWidth: "100%", inputHeight: "400px"},
          {kind: "WebLetter.TextToken", key: "closing", description: "Closing"},
          {kind: "WebLetter.TextToken", key: "signatureaddition", description: "Signature Addition", isOptional: true, isEnabled: false}
          ]
        },
        {tag: "form", name: "pdfform", showing: false},
          {kind: "onyx.Button", name: "go", content: "Generate PDF", ontap: "go"}
        ]
      }
		]}
	],
  create: function() {
    this.tokens = new Array();
    this.inherited(arguments);
    // set up form
    this.$.pdfform.attributes.method=this.$.config.getMethod();
    this.$.pdfform.attributes.action=this.$.config.getServerUrl();
  },
  handlers: {
    onNewToken: "registerToken",
    onDisableToken: "deregisterToken"
  },
  registerToken: function(inSender, inEvent) {
    this.tokens.push(inEvent.originator);
    return true;
  },
  deregisterToken: function(inSender, inEvent) {
    // do nothing since disabled tokens might be optional and
    // need the %nopt-token to be replaced
    return true;
  },
  go: function() {
    //this.$.mylogger.addContent(this.$.lettercontent.getValue());
    this.$.pdfform.destroyComponents();
    for (i in this.tokens) {
      if (this.tokens[i].isEnabled) {
        // add opt-token if the token is optional
        if (this.tokens[i].isOptional) {
          var key = "%opt-" + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: "input", attributes: {name: key, value: ""}});
        }
        if (this.tokens[i].topToken) {
          var type;
          var content = "";
          var value = "";
          if (this.tokens[i].multiline) {
            type = "textarea";
            content = this.tokens[i].getInput();
          } else {
            type = "input";
            value = this.tokens[i].getInput();
          }
          var keyprefix = "token-";
          var key = keyprefix + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: type, attributes: {name: key, value: value}, content: content});
        }
      }
      else { // !this.tokens[i].isEnabled
        // only add the nopt token
        if (this.tokens[i].isOptional) {
          var key = "%nopt-" + this.tokens[i].key;
          this.$.pdfform.createComponent({tag: "input", attributes: {name: key, value: ""}});
        }
      }
    }
    this.$.pdfform.render();
    if (this.$.config.getDebugging()) {
      var components = this.$.pdfform.getComponents();
      for (i in components) {
        var attrs = components[i].attributes;
        enyo.log("'" + attrs.name + "': '" + attrs.value + "'");
      }
    }
    else {
      document.getElementById(this.$.pdfform.getAttribute("id")).submit();
    }
  }

});
<|endoftext|>


<commit_before>define([
  'common/views/govuk',
  'extensions/views/view',
  'extensions/models/model'
],
function (GovUkView, View, Model) {
  describe('GovUkView', function () {

    var model;
    beforeEach(function () {
      model = new Model({
        requirePath: '/testRequirePath/',
        assetPath: '/testAssetPath/',
        assetDigest: {
          'spotlight.css': 'spotlight-cachebust.css'
        },
        environment: 'development'
      });
    });

    it('renders a page with breadcrumbs and content view in GOV.UK style', function () {

      var view = new GovUkView({
        model: model,
        bodyEndTemplate: function () {
          return 'body_end';
        },
        reportAProblemTemplate: function () {
          return 'report_a_problem';
        }
      });

      spyOn(view, 'template').andReturn('rendered');
      spyOn(view, 'getContent').andReturn('test_content');

      view.render();

      expect(view.html).toEqual('rendered');

      var context = view.template.argsForCall[0][0];
      expect(context.head.trim().indexOf('<link href="/testAssetPath/stylesheets/spotlight-cachebust.css" media="screen" rel="stylesheet" type="text/css">')).not.toBe(-1);
      expect(context.head.trim().indexOf('google-analytics.com')).not.toBe(-1);
      expect(context.bodyEnd).toEqual('body_end');
      expect(context.pageTitle).toEqual('Performance - GOV.UK');

      var content = context.content.replace(/\s+/g, ' ').trim();
      expect(content).toEqual('

   Performance

 test_content report_a_problem

');
    });

    it('doesn\'t display the breadcrumb wrapper if there are no breadcrumbs', function () {
      var view = new GovUkView({
        model: model,
      });

      spyOn(view, 'template').andReturn('rendered');
      spyOn(view, 'getBreadcrumbCrumbs').andReturn([]);

      view.render();

      var context = view.template.argsForCall[0][0];
      var content = context.content.replace(/\s+/g, ' ').trim();

      expect(content.indexOf('breadcrumb')).toEqual(-1);
    });

    it("adds ellipses to very long breadcrumbs", function () {
      var ellipsisView = new GovUkView({
        model: model,
      });

      spyOn(ellipsisView, 'template').andReturn('rendered')
      spyOn(ellipsisView, 'getBreadcrumbCrumbs').andReturn([
        {'path': '/performance', 'title': 'Performance'},
        {'path': '/url', 'title': 'A very very very very very very very long department name'}
      ]);

      ellipsisView.render();

      var context = ellipsisView.template.argsForCall[0][0];
      var content = context.content.replace(/\s+/g, ' ').trim();

      expect(content).toContain('A very very very very very very …');
    });

  });
});
<commit_msg>Fixed lint errors for this file.<commit_after>define([
  'common/views/govuk',
  'extensions/views/view',
  'extensions/models/model'
],
function (GovUkView, View, Model) {
  describe('GovUkView', function () {

    var model;
    beforeEach(function () {
      model = new Model({
        requirePath: '/testRequirePath/',
        assetPath: '/testAssetPath/',
        assetDigest: {
          'spotlight.css': 'spotlight-cachebust.css'
        },
        environment: 'development'
      });
    });

    it('renders a page with breadcrumbs and content view in GOV.UK style', function () {

      var view = new GovUkView({
        model: model,
        bodyEndTemplate: function () {
          return 'body_end';
        },
        reportAProblemTemplate: function () {
          return 'report_a_problem';
        }
      });

      spyOn(view, 'template').andReturn('rendered');
      spyOn(view, 'getContent').andReturn('test_content');

      view.render();

      expect(view.html).toEqual('rendered');

      var context = view.template.argsForCall[0][0];
      expect(context.head.trim().indexOf('<link href="/testAssetPath/stylesheets/spotlight-cachebust.css" media="screen" rel="stylesheet" type="text/css">')).not.toBe(-1);
      expect(context.head.trim().indexOf('google-analytics.com')).not.toBe(-1);
      expect(context.bodyEnd).toEqual('body_end');
      expect(context.pageTitle).toEqual('Performance - GOV.UK');

      var content = context.content.replace(/\s+/g, ' ').trim();
      expect(content).toEqual('

   Performance

 test_content report_a_problem

');
    });

    it('doesn\'t display the breadcrumb wrapper if there are no breadcrumbs', function () {
      var view = new GovUkView({
        model: model,
      });

      spyOn(view, 'template').andReturn('rendered');
      spyOn(view, 'getBreadcrumbCrumbs').andReturn([]);

      view.render();

      var context = view.template.argsForCall[0][0];
      var content = context.content.replace(/\s+/g, ' ').trim();

      expect(content.indexOf('breadcrumb')).toEqual(-1);
    });

    it('adds ellipses to very long breadcrumbs', function () {
      var ellipsisView = new GovUkView({
        model: model,
      });

      spyOn(ellipsisView, 'template').andReturn('rendered');
      spyOn(ellipsisView, 'getBreadcrumbCrumbs').andReturn([
        {'path': '/performance', 'title': 'Performance'},
        {'path': '/url', 'title': 'A very very very very very very very long department name'}
      ]);

      ellipsisView.render();

      var context = ellipsisView.template.argsForCall[0][0];
      var content = context.content.replace(/\s+/g, ' ').trim();

      expect(content).toContain('A very very very very very very …');
    });

  });
});
<|endoftext|>


<commit_before>import {task, start, isEmpty} from '../src'
import Container from '../src/domain/container'
import {containerRepository, containerFactory} from './helper'

import {expect} from 'chai'

describe('isEmpty', () => {

  it('returns true if the tasks are empty', () => {

    expect(isEmpty()).to.be.true

  })

  it('returns false if the tasks are not empty', () => {

    task('test')

    expect(isEmpty()).to.be.false

  })

})

describe('task(taksName).create(params).do(action)', () => {

  it('creates the container with the given params and perform the action on it', (done) => {

    task('foo')
    .create({name: 'foo0', image: 'ubuntu', cmd: 'vi', ports: '9100:9100'})
    .do(container => container.start())

    start(['foo'], (err) => {

      if (err) {

        return done(err)

      }

      containerRepository.getByName('foo0')

      .then(container => {

        expect(container).to.be.instanceof(Container)
        expect(container.name).to.equal('/foo0')
        expect(container.image).to.equal('ubuntu')
        expect(container.cmd).to.equal('vi')
        expect(container.ports).to.eql(['9100:9100'])
        expect(container.isRunning).to.be.true

        container.remove()

      })

      .then(() => done()).catch(done)

    })

  })

  it('can add multiple container-action pairs', done => {

    task('multi-create')
    .create({
      name: 'multi-create0',
      image: 'ubuntu',
      cmd: 'vi',
      ports: '9123:9123'
    })
    .do(container => container.start())
    .create({
      name: 'multi-create1',
      image: 'ubuntu',
      cmd: 'vi',
      ports: '9124:9124'
    })
    .do(container => container.start())

    start(['multi-create'], err => {

      if (err) {

        return done(err)

      }

      Promise.all([
        containerRepository.getByName('multi-create0').then(container => {

          expect(container).to.exist
          containerRepository.remove(container)

        }),
        containerRepository.getByName('multi-create1').then(container => {

          expect(container).to.exist
          containerRepository.remove(container)

        }),
      ]).then(() => done(), done)

    })

  })

})

describe('task(taksName).get(name).do(action)', () => {

  it('gets the container of the name and performs the action on it', (done) => {

    task('remove')
    .get('foo1')
    .do(container => container.remove())

    const container = containerFactory.createFromDslObject({
      name: 'foo1',
      image: 'ubuntu',
      cmd: 'vi'
    })

    containerRepository.save(container)

    .then(() => container.start())

    .then(() => new Promise(resolve => start(['remove'], (err) => {

      if (err) {

        throw err

      }

      resolve(containerRepository.getByName('foo1'))

    }))).then(container => {

      expect(container).to.be.null

    }).then(() => done(), done)

  })

  it('throws when the container not found', (done) => {

    task('not-found')
    .get('not-found')
    .do(container => container.anything())

    start(['not-found'], (err) => {

      try {

        expect(err).to.be.instanceof(Error)
        expect(err.message).to.equal('Cannot get container of the name: not-found')

        done()

      } catch (e) {

        reject(e)

      }

    })

  })

  it('does not throws if the quiet flag is true, and the action is skipped', (done) => {

    task('not-found')
    .get('not-found', {quiet: true})
    .do(container => container.anything())

    start(['not-found'], err => {

      try {

        expect(err).to.equal(null)

        done()

      } catch (e) {

        done(e)

      }

    })

  })

  it('can get multiple items when an array is given', done => {

    task('multiple-get')
    .create({image: 'ubuntu', cmd: 'vi', name: 'm-get0'})
    .create({image: 'ubuntu', cmd: 'vi', name: 'm-get1'})
    .do(container => container.start())
    .get(['m-get0', 'm-get1'])
    .do(container => container.remove())

    start(['multiple-get'], err => {

      if (err) { return done(err) }

      done()

    })

  })

})
<commit_msg>Fix error handling<commit_after>import {task, start, isEmpty} from '../src'
import Container from '../src/domain/container'
import {containerRepository, containerFactory} from './helper'

import {expect} from 'chai'

describe('isEmpty', () => {

  it('returns true if the tasks are empty', () => {

    expect(isEmpty()).to.be.true

  })

  it('returns false if the tasks are not empty', () => {

    task('test')

    expect(isEmpty()).to.be.false

  })

})

describe('task(taksName).create(params).do(action)', () => {

  it('creates the container with the given params and perform the action on it', (done) => {

    task('foo')
    .create({name: 'foo0', image: 'ubuntu', cmd: 'vi', ports: '9100:9100'})
    .do(container => container.start())

    start(['foo'], (err) => {

      if (err) {

        return done(err)

      }

      containerRepository.getByName('foo0')

      .then(container => {

        expect(container).to.be.instanceof(Container)
        expect(container.name).to.equal('/foo0')
        expect(container.image).to.equal('ubuntu')
        expect(container.cmd).to.equal('vi')
        expect(container.ports).to.eql(['9100:9100'])
        expect(container.isRunning).to.be.true

        container.remove()

      })

      .then(() => done()).catch(done)

    })

  })

  it('can add multiple container-action pairs', done => {

    task('multi-create')
    .create({
      name: 'multi-create0',
      image: 'ubuntu',
      cmd: 'vi',
      ports: '9123:9123'
    })
    .do(container => container.start())
    .create({
      name: 'multi-create1',
      image: 'ubuntu',
      cmd: 'vi',
      ports: '9124:9124'
    })
    .do(container => container.start())

    start(['multi-create'], err => {

      if (err) {

        return done(err)

      }

      Promise.all([
        containerRepository.getByName('multi-create0').then(container => {

          expect(container).to.exist
          containerRepository.remove(container)

        }),
        containerRepository.getByName('multi-create1').then(container => {

          expect(container).to.exist
          containerRepository.remove(container)

        }),
      ]).then(() => done(), done)

    })

  })

})

describe('task(taksName).get(name).do(action)', () => {

  it('gets the container of the name and performs the action on it', (done) => {

    task('remove')
    .get('foo1')
    .do(container => container.remove())

    const container = containerFactory.createFromDslObject({
      name: 'foo1',
      image: 'ubuntu',
      cmd: 'vi'
    })

    containerRepository.save(container)

    .then(() => container.start())

    .then(() => new Promise((resolve, reject) => start(['remove'], (err) => {

      if (err) {

        return reject(err)

      }

      resolve(containerRepository.getByName('foo1'))

    }))).then(container => {

      expect(container).to.be.null

    }).then(() => done(), done)

  })

  it('throws when the container not found', (done) => {

    task('not-found')
    .get('not-found')
    .do(container => container.anything())

    start(['not-found'], (err) => {

      try {

        expect(err).to.be.instanceof(Error)
        expect(err.message).to.equal('Cannot get container of the name: not-found')

        done()

      } catch (e) {

        reject(e)

      }

    })

  })

  it('does not throws if the quiet flag is true, and the action is skipped', (done) => {

    task('not-found')
    .get('not-found', {quiet: true})
    .do(container => container.anything())

    start(['not-found'], err => {

      try {

        expect(err).to.equal(null)

        done()

      } catch (e) {

        done(e)

      }

    })

  })

  it('can get multiple items when an array is given', done => {

    task('multiple-get')
    .create({image: 'ubuntu', cmd: 'vi', name: 'm-get0'})
    .create({image: 'ubuntu', cmd: 'vi', name: 'm-get1'})
    .do(container => container.start())
    .get(['m-get0', 'm-get1'])
    .do(container => container.remove())

    start(['multiple-get'], err => {

      if (err) { return done(err) }

      done()

    })

  })

})
<|endoftext|>


<commit_before>var spawn = require('child_process').spawn;
var path = require('path');

module.exports = function (options) {
    options = options || {};
    options.url = options.url || 'http://nodejs.org';
    options.name = options.name || 'nodejs';

    var client;
    switch (process.platform) {
    case 'win32':
        client = path.resolve(__dirname + '/cefclient/cefclient');
        break;
    case 'linux':
        client = path.resolve(__dirname + '/build/default/topcube');
        break;
    default:
        console.warn('');
        return null;
        break;
    }

    var args = [];
    for (var key in options) args.push('--' + key + '=' + options[key]);

    var child = spawn(client, args);
    child.on('exit', function(code) {
        process.exit(code);
    });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    return child;
};

<commit_msg>Define valid keys per platform and filter out invalid options.<commit_after>var spawn = require('child_process').spawn;
var path = require('path');

module.exports = function (options) {
    options = options || {};
    options.url = options.url || 'http://nodejs.org';
    options.name = options.name || 'nodejs';

    var client;
    var keys = [];
    switch (process.platform) {
    case 'win32':
        client = path.resolve(__dirname + '/cefclient/cefclient');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight', 'ico'];
        break;
    case 'linux':
        client = path.resolve(__dirname + '/build/default/topcube');
        keys = ['url', 'name', 'width', 'height', 'minwidth', 'minheight'];
        break;
    default:
        console.warn('');
        return null;
        break;
    }

    var args = [];
    for (var key in options) {
        if (keys.indexOf(key) !== -1) {
            args.push('--' + key + '=' + options[key]);
        }
    }

    var child = spawn(client, args);
    child.on('exit', function(code) {
        process.exit(code);
    });
    child.stdout.pipe(process.stdout);
    child.stderr.pipe(process.stderr);
    return child;
};

<|endoftext|>


<commit_before>var table = document.getElementById('tracker');
var headings = document.getElementById('headings');
var button = document.querySelectorAll('button');
// var form = document.querySelector('form');
var inputs = document.querySelectorAll('input');
var added = [];
button[0].addEventListener('click', submit);
for (var i = 0; i < inputs.length; i++) {
	inputs[i].addEventListener('keypress', function(event) {
		if (event.keyCode == 13) {
			submit();
		}
	});
}

function submit() {
	var item = inputs[0].value;
	var row = document.createElement('tr');
	if (added.length == 0 || added.indexOf(item) == -1) {
		added.push(item);
		createRow(row);
		deleteButton(row);
		table.appendChild(row);

	} else {
		var data = table.querySelectorAll('tr:not(#headings)');
		Array.prototype.forEach.call(data, function(row) {
			if (row.childNodes[0].textContent == item) {
				var newRow = document.createElement('tr');
				updateRow(newRow, row)
				deleteButton(newRow);
				table.replaceChild(newRow, row);
			}
		});
	}

}

function createRow(row) {
	for (var i = 0; i < inputs.length; i++) {
			var cell = document.createElement('td');
			cell.textContent = inputs[i].value;
			row.appendChild(cell);
			inputs[i].value = '';
	}
	inputs[0].focus();
}

function updateRow(row, old) {
	for (i = 0; i < inputs.length; i++) {
		var cell = document.createElement('td');
		if (i == 1 || i == 2) {
			if (isNaN(parseInt(old.children[i].textContent)) || isNaN(parseInt(inputs[i].value))) {
				cell.textContent = inputs[i].value;
			} else {
			cell.textContent = parseInt(old.children[i].textContent) + parseInt(inputs[i].value);
			}
		}
		else {
			cell.textContent = inputs[i].value;
		}
		row.appendChild(cell);
		inputs[i].value = '';
	}
	inputs[0].focus()
}

function deleteButton(row) {
	var del = document.createElement('td');
		del.appendChild(document.createElement('button'));
		del.childNodes[0].textContent = "Delete";
		del.className = "delete";
		row.appendChild(del);
		del.addEventListener('click', function() {
			added.splice(added.indexOf(row.children[0].textContent))
			table.removeChild(row);
		});
}<commit_msg>Improve code that updates existing row<commit_after>var table = document.getElementById('tracker');
var headings = document.getElementById('headings');
var button = document.querySelectorAll('button');
// var form = document.querySelector('form');
var inputs = document.querySelectorAll('input');
var added = [];
button[0].addEventListener('click', submit);
for (var i = 0; i < inputs.length; i++) {
	inputs[i].addEventListener('keypress', function(event) {
		if (event.keyCode == 13) {
			submit();
		}
	});
}

function submit() {
	var item = inputs[0].value;
	var row = document.createElement('tr');
	if (added.length == 0 || added.indexOf(item) == -1) {
		added.push(item);
		createRow(row);
		deleteButton(row);
		table.appendChild(row);

	} else {
		var data = table.querySelectorAll('tr:not(#headings)');
		Array.prototype.forEach.call(data, function(row) {
			if (row.childNodes[0].textContent == item) {
				var newRow = document.createElement('tr');
				updateRow(newRow, row)
				deleteButton(newRow);
				table.replaceChild(newRow, row);
				return;
			}
		});
	}

}

function createRow(row) {
	for (var i = 0; i < inputs.length; i++) {
			var cell = document.createElement('td');
			cell.textContent = inputs[i].value;
			row.appendChild(cell);
			inputs[i].value = '';
	}
	inputs[0].focus();
}

function updateRow(row, old) {
	for (i = 0; i < inputs.length; i++) {
		var cell = document.createElement('td');
		if (i == 1 || i == 2) {
			if (isNaN(parseInt(old.children[i].textContent)) || isNaN(parseInt(inputs[i].value))) {
				cell.textContent = inputs[i].value;
			} else {
			cell.textContent = parseInt(old.children[i].textContent) + parseInt(inputs[i].value);
			}
		}
		else {
			cell.textContent = inputs[i].value;
		}
		row.appendChild(cell);
		inputs[i].value = '';
	}
	inputs[0].focus()
}

function deleteButton(row) {
	var del = document.createElement('td');
		del.appendChild(document.createElement('button'));
		del.childNodes[0].textContent = "Delete";
		del.className = "delete";
		row.appendChild(del);
		del.addEventListener('click', function() {
			added.splice(added.indexOf(row.children[0].textContent))
			table.removeChild(row);
		});
}
<|endoftext|>


<commit_before>var MongoClient = require('mongodb').MongoClient
var url = "mongodb://localhost:27017/TheOrangeAlliance"

var apiKey = require('../config/apiKey.js')
var axios = require('axios')
var toaApi = axios.create({
	baseURL: 'http://theorangealliance.org/apiv2/',
	timeout: 10000,
	headers: {'X-Application-Origin': 'JuicyData', 'X-TOA-Key': apiKey}
})
var eventKeys = ['1718-NCAL-RWC']

module.exports = function() {
	getData()
}

function getData() {
	MongoClient.connect(url, function(err, db) {
		if (err) throw err;
		for (let eventKey of eventKeys) {
			toaApi.get('/event/' + eventKey).then(function(response) {
				let event = response.data[0];
				let eventInformation = {
					name: event.event_name,
					date: event.start_date, //Should be ISODate Type thingy....? (need to check latter)
					locationID: 'Lol..' //ObjectId Type; need to querry database for this location ID....
				}
				// let eventInformation:{
				// 	date: ISODate(), //ISO Date of when it occured;
				// 	locationID: ObjectId() //ID of the location in the 'places' collection
				// }
				toaApi.get('/event/' + eventKey + '/matches').then(function(response) {
					let matches = response.data;
					let matchNumbers = []
					let relevantMatches = {}
					let currentMatchKey
					let matchDatas = {}
					let gameDatas = {red: {}, blue: {}}
					for (let match of matches) {
						let matchNumber = match.match_name.split('Quals ')[1]
						if (matchNumber) {
							if (!matchNumbers.includes(matchNumber)) {
								matchNumbers.push(Number(matchNumber))
							}
							relevantMatches[matchNumber] = match
						}
					}
					for (let matchNumber of matchNumbers) {
						let match = relevantMatches[matchNumber]
						currentMatchKey = match.match_key
						toaApi.get('/match/' + match.match_key + '/stations').then(function(response) {
							let stations = response.data
							let teams = {
								red: [],
								blue: []
							}
							for (let station of stations) {
								allianceCode = station.station_key.charAt(station.station_key.length-2)
								if (allianceCode === 'R') {
									teams.red.push(Number(station.team_key))
								} else if (allianceCode === 'B') {
									teams.blue.push(Number(station.team_key))
								}
							}

							let matchData = {
								_id:{
									eventInformation: eventInformation,
									matchInformation:{
										matchNumber: Number(matchNumber),
										teams:{	//NOT SURE IF WE ACUALLY NEED THIS; WE HAVE SCHEDULE WHICH SHOULD BE A MATCH OF THIS
											red1: teams.red[0],
											red2: teams.red[1],
											blue1: teams.blue[0],
											blue2: teams.blue[1]
										}
									}
								},
								resultInformation:{
									winner: match.red_score > match.blue_score ? 'red' : match.blue_score > match.red_score ? 'blue' : 'tie', //Pls check latter if "score" indlues penailtyes for this caluclation
									score:{
										auto:{
											red: match.red_auto_score,
											blue: match.blue_auto_score
										},
										driver:{
											red: match.red_tele_score,
											blue: match.blue_tele_score
										},
										end:{
											red: match.red_end_score,
											blue: match.blue_end_score
										},
										total:{
											// red: match.red_score - match.blue_penalty, //I think that blue penialty is the penailty blue gets
											// blue: match.blue_score - match.red_penalty //And score is their total (penailty + score)
											red: match.red_auto_score + match.red_tele_score + match.red_end_score,
											blue: match.blue_auto_score + match.blue_tele_score + match.blue_end_score
										},
										penalty:{
											red: match.red_penalty,
											blue: match.blue_penalty
										},
										final:{
											red: match.red_score,
											blue: match.blue_score
										}
									}
								}
							}
							insert(db, 'matchData', matchData)
							matchDatas[matchNumber] = matchData

							toaApi.get('/match/' + match.match_key + '/details').then(function(response) {
								let matchDetails = response.data[0]
								for (let alliance of ['red', 'blue']) {
									let gameData = {
										_id:{
											eventInformation: eventInformation,
											matchInformation:{
												matchNumber: matchNumber,
												robotAlliance: alliance, //blue or red; with lower case
												teams: teams[alliance]
											}
										},
										gameInformation:{	//CHECK IF ALL THSES TYPES ARE CORRECT AND ALSO HAVE COFRRECT MEANING!
											auto:{
												jewel: matchDetails[alliance+'_auto_jewel'],
												glyphs: matchDetails[alliance+'_auto_glyphs'],
												keys: matchDetails[alliance+'_auto_keys'],
												park: matchDetails[alliance+'_auto_park']
											},
											driver:{
												glyphs: matchDetails[alliance+'_tele_glyphs'],
												rows: matchDetails[alliance+'_tele_rows'],
												columns: matchDetails[alliance+'_tele_columns'],
												cypher: matchDetails[alliance+'_tele_cypher']
											},
											end:{
												relic1: matchDetails[alliance+'_end_relic_one'],	//Amount of relics in that zone
												relic2: matchDetails[alliance+'_end_relic_two'],
												relic3: matchDetails[alliance+'_end_relic_three'],
												relicsUp: matchDetails[alliance+'_end_relic_up'],	//Amount of relects standing up
												balanced: matchDetails[alliance+'_end_robot_bal']	//How many robots are balanced
											}
										}
									}
									insert(db, 'gameData', gameData)
									gameDatas[alliance][matchNumber] = gameData
									if (gameDataIsComplete(matchNumbers, gameDatas)) {
										printDatas(eventInformation.name, matchNumbers, matchDatas, gameDatas)
										db.close()
									}
								}
							})
						})
					}
				})
			})
		}
	})
	// setTimeout(getData, 180000) Do it all again in 3 minutes
}

function insert(db, collectionName, data) {
  	db.collection(collectionName).insertOne(data, function(err, res) {
    	if (err) { //TODO: Make better way to check if data already exists. Doing this requires the least amount of code, however
    		if (err.code == 11000) { // Duplicate key error
    			console.log('Skipping data. Document with _id "' + JSON.stringify(data._id) + '" already exists.')
    		} else {
    			throw err
    		}
    	}
  	});
}

function printDatas(eventName, matchNumbers, matchDatas, gameDatas) {
	for (let matchNumber of matchNumbers) {
		console.log(eventName + ' Match ' + matchNumber + ' matchData:')
		console.log(JSON.stringify(matchDatas[matchNumber], null, 2))
		console.log()
		console.log(eventName + ' Match ' + matchNumber + ' red gameData:')
		console.log(JSON.stringify(gameDatas.red[matchNumber], null, 2))
		console.log()
		console.log(eventName + ' Match ' + matchNumber + ' blue gameData:')
		console.log(JSON.stringify(gameDatas.blue[matchNumber], null, 2))
		console.log()
		console.log()
	}
}

function gameDataIsComplete(matchNumbers, gameDatas) {
	for (let matchNumber of matchNumbers) {
		if (!gameDatas.red[matchNumber] || !gameDatas.blue[matchNumber]) {
			return false
		}
	}
	return true
}<commit_msg>Changed MongoDB insert to save<commit_after>var MongoClient = require('mongodb').MongoClient
var url = "mongodb://localhost:27017/TheOrangeAlliance"

var apiKey = require('../config/apiKey.js')
var axios = require('axios')
var toaApi = axios.create({
	baseURL: 'http://theorangealliance.org/apiv2/',
	timeout: 10000,
	headers: {'X-Application-Origin': 'JuicyData', 'X-TOA-Key': apiKey}
})
var eventKeys = ['1718-NCAL-RWC']

module.exports = function() {
	getData()
}

function getData() {
	MongoClient.connect(url, function(err, db) {
		if (err) throw err;
		for (let eventKey of eventKeys) {
			toaApi.get('/event/' + eventKey).then(function(response) {
				let event = response.data[0];
				let eventInformation = {
					name: event.event_name,
					date: event.start_date, //Should be ISODate Type thingy....? (need to check latter)
					locationID: 'Lol..' //ObjectId Type; need to querry database for this location ID....
				}
				// let eventInformation:{
				// 	date: ISODate(), //ISO Date of when it occured;
				// 	locationID: ObjectId() //ID of the location in the 'places' collection
				// }
				toaApi.get('/event/' + eventKey + '/matches').then(function(response) {
					let matches = response.data;
					let matchNumbers = []
					let relevantMatches = {}
					let currentMatchKey
					let matchDatas = {}
					let gameDatas = {red: {}, blue: {}}
					for (let match of matches) {
						let matchNumber = match.match_name.split('Quals ')[1]
						if (matchNumber) {
							if (!matchNumbers.includes(matchNumber)) {
								matchNumbers.push(Number(matchNumber))
							}
							relevantMatches[matchNumber] = match
						}
					}
					for (let matchNumber of matchNumbers) {
						let match = relevantMatches[matchNumber]
						currentMatchKey = match.match_key
						toaApi.get('/match/' + match.match_key + '/stations').then(function(response) {
							let stations = response.data
							let teams = {
								red: [],
								blue: []
							}
							for (let station of stations) {
								allianceCode = station.station_key.charAt(station.station_key.length-2)
								if (allianceCode === 'R') {
									teams.red.push(Number(station.team_key))
								} else if (allianceCode === 'B') {
									teams.blue.push(Number(station.team_key))
								}
							}

							let matchData = {
								_id:{
									eventInformation: eventInformation,
									matchInformation:{
										matchNumber: Number(matchNumber),
										teams:{	//NOT SURE IF WE ACUALLY NEED THIS; WE HAVE SCHEDULE WHICH SHOULD BE A MATCH OF THIS
											red1: teams.red[0],
											red2: teams.red[1],
											blue1: teams.blue[0],
											blue2: teams.blue[1]
										}
									}
								},
								resultInformation:{
									winner: match.red_score > match.blue_score ? 'red' : match.blue_score > match.red_score ? 'blue' : 'tie', //Pls check latter if "score" indlues penailtyes for this caluclation
									score:{
										auto:{
											red: match.red_auto_score,
											blue: match.blue_auto_score
										},
										driver:{
											red: match.red_tele_score,
											blue: match.blue_tele_score
										},
										end:{
											red: match.red_end_score,
											blue: match.blue_end_score
										},
										total:{
											// red: match.red_score - match.blue_penalty, //I think that blue penialty is the penailty blue gets
											// blue: match.blue_score - match.red_penalty //And score is their total (penailty + score)
											red: match.red_auto_score + match.red_tele_score + match.red_end_score,
											blue: match.blue_auto_score + match.blue_tele_score + match.blue_end_score
										},
										penalty:{
											red: match.red_penalty,
											blue: match.blue_penalty
										},
										final:{
											red: match.red_score,
											blue: match.blue_score
										}
									}
								}
							}
							save(db, 'matchData', matchData)
							matchDatas[matchNumber] = matchData

							toaApi.get('/match/' + match.match_key + '/details').then(function(response) {
								let matchDetails = response.data[0]
								for (let alliance of ['red', 'blue']) {
									let gameData = {
										_id:{
											eventInformation: eventInformation,
											matchInformation:{
												matchNumber: matchNumber,
												robotAlliance: alliance, //blue or red; with lower case
												teams: teams[alliance]
											}
										},
										gameInformation:{	//CHECK IF ALL THSES TYPES ARE CORRECT AND ALSO HAVE COFRRECT MEANING!
											auto:{
												jewel: matchDetails[alliance+'_auto_jewel'],
												glyphs: matchDetails[alliance+'_auto_glyphs'],
												keys: matchDetails[alliance+'_auto_keys'],
												park: matchDetails[alliance+'_auto_park']
											},
											driver:{
												glyphs: matchDetails[alliance+'_tele_glyphs'],
												rows: matchDetails[alliance+'_tele_rows'],
												columns: matchDetails[alliance+'_tele_columns'],
												cypher: matchDetails[alliance+'_tele_cypher']
											},
											end:{
												relic1: matchDetails[alliance+'_end_relic_one'],	//Amount of relics in that zone
												relic2: matchDetails[alliance+'_end_relic_two'],
												relic3: matchDetails[alliance+'_end_relic_three'],
												relicsUp: matchDetails[alliance+'_end_relic_up'],	//Amount of relects standing up
												balanced: matchDetails[alliance+'_end_robot_bal']	//How many robots are balanced
											}
										}
									}
									save(db, 'gameData', gameData)
									gameDatas[alliance][matchNumber] = gameData
									if (gameDataIsComplete(matchNumbers, gameDatas)) {
										printDatas(eventInformation.name, matchNumbers, matchDatas, gameDatas)
										db.close()
									}
								}
							})
						})
					}
				})
			})
		}
	})
	// setTimeout(getData, 180000) Do it all again in 3 minutes
}

function save(db, collectionName, data) {
  	db.collection(collectionName).save(data, function(err, res) {
    	if (err) throw err
  	});
}

function printDatas(eventName, matchNumbers, matchDatas, gameDatas) {
	for (let matchNumber of matchNumbers) {
		console.log(eventName + ' Match ' + matchNumber + ' matchData:')
		console.log(JSON.stringify(matchDatas[matchNumber], null, 2))
		console.log()
		console.log(eventName + ' Match ' + matchNumber + ' red gameData:')
		console.log(JSON.stringify(gameDatas.red[matchNumber], null, 2))
		console.log()
		console.log(eventName + ' Match ' + matchNumber + ' blue gameData:')
		console.log(JSON.stringify(gameDatas.blue[matchNumber], null, 2))
		console.log()
		console.log()
	}
}

function gameDataIsComplete(matchNumbers, gameDatas) {
	for (let matchNumber of matchNumbers) {
		if (!gameDatas.red[matchNumber] || !gameDatas.blue[matchNumber]) {
			return false
		}
	}
	return true
}<|endoftext|>


<commit_before>var RankController = function(configSource) {
    this._configSource = configSource;
    this._sortedRanks = this._sortedRanks(this._configSource.getRanks());
    var Datastore = require('nedb');
    this._db = new Datastore({
        filename: 'ranks.db',
        autoload: true
    });
    this._db.ensureIndex({ fieldName: 'name', unique: true }, function (err) {
        if (err) {
            alert("Can't create indexes");
        }
    });

    this._importUsers();
};

RankController.prototype.processMessage = function (message, options, callback) {
    var expGain;
    if (options.firstMessage) {
        expGain = this._configSource.getFirstMessageExperience();
    } else if (options.smileOnly) {
        expGain = this._configSource.getSmileExperience();
    } else {
        expGain = this._configSource.getMessageExperience();
    }
    this._db.update({ name: message.nickname }, { $inc: { exp: expGain } }, { upsert: true });
    this._db.find({ name: message.nickname }, function (err, users) {
        if (err || users === undefined || users.length < 1) {
            return;
        }
        var user = users[0];
        var rankChanged = this._updateRank(user);
        callback(rankChanged, user);
    }.bind(this));
};

RankController.prototype.getRankId = function (username, callback) {
    this._db.find({ name: username }, function (err, users) {
        if (err) {
            callback(undefined);
            return;
        }
        if (users.length > 0) {
            callback(users[0].rankId);
            return;
        }
        callback(undefined);
    });
};

RankController.prototype.getRankById = function(rankId) {
    return this._configSource.getRanks()[rankId];
};

RankController.prototype._configSource = null;
RankController.prototype._db = null;
RankController.prototype._sortedRanks = null;

RankController.prototype._updateRank = function (user) {
    var newRankId = this._getNextRank(user.rankId, user.exp);
    if (newRankId !== user.rankId) {
        user.rankId = newRankId;
        this._db.update({ name: user.name }, { $set: { rankId: user.rankId } }, {});
        return user.rankId !== this._configSource.getDefaultRankId();
    }
    return false;
};

RankController.prototype._sortedRanks = function(ranks) {
    var unsortedRanks = [];
    var rankIds = Object.keys(ranks);
    rankIds.forEach(function(rankId, index) {
        var rank = ranks[rankId];
        var rankCopy = {
            "exp": rank.exp,
            "icon": rank.icon,
            "title": rank.title
        };
        rankCopy.id = rankId;
        unsortedRanks.push(rankCopy);
    });
    var sortedRanks = unsortedRanks.sort(function(a, b) {
        return a.exp - b.exp;
    });
    return sortedRanks;
};

RankController.prototype._getNextRank = function (currentRankId, exp) {
    if (currentRankId !== undefined) {
        if (this._configSource.getRanks()[currentRankId].exp < 0) {
            return currentRankId;
        }
        if (this._configSource.getRanks()[currentRankId].exp > exp) {
            exp = this._configSource.getRanks()[currentRankId].exp;
        }
    }
    var iRank;
    for (iRank = 0; iRank < this._sortedRanks.length; ++iRank) {
        if (this._sortedRanks[iRank].exp > exp) {
            break;
        }
    }

    if (iRank > 0) {
        return this._sortedRanks[iRank - 1].id;
    } else {
        return undefined;
    }
};

RankController.prototype._importUsers = function () {
    var usersFileName = "ranks.json";
    var conversionFileName = "ranksToNewRanks.json";
    var fs = require("fs");
    if (!fs.existsSync(usersFileName)) {
        return;
    }
    if (!fs.existsSync(conversionFileName)) {
        return;
    }
    var usersData = fs.readFileSync(usersFileName);
    var importedUsers = JSON.parse(usersData);
    var conversionData = fs.readFileSync(conversionFileName);
    var rankToNewRank = JSON.parse(conversionData);

    var usersPerLevel = {};
    var maxExp = 0;
    var maxExpName = null;
    importedUsers.forEach( function( user )
    {
        usersPerLevel[user.level] = usersPerLevel[user.level] || 0;
        usersPerLevel[user.level]++;
        if (user.exp > maxExp) {
            maxExp = user.exp;
            maxExpName = user.name;
        }

        // Conversion
        user.exp *= 10;
        var name = user.name;
        user.name = user.name.replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '');
        user.name = user.name.trim();
        if (user.name.length == 0) {
            console.log(name);
            return;
        }

        var convertedRankId = rankToNewRank[user.level];
        var convertedRank = this._configSource.getRanks()[convertedRankId];
        var convertedExp = user.exp > convertedRank.exp ? convertedRank.exp : user.exp;

        var convertedUser = new User();
        convertedUser.name = user.name;
        convertedUser.rankId = convertedRankId;
        convertedUser.exp = convertedExp;

        this._db.update({ name: user.name }, convertedUser, { upsert: true }, function (err, numReplaced, upsert) {

        });
    }.bind(this));
    var ranksStats = {
        "usersCount": importedUsers.length,
        "hightestRank": {
            "name": maxExpName,
            "exp": maxExp
        },
        "usersPerLevel": usersPerLevel
    };
    console.log(JSON.stringify(ranksStats, null, 2));
};<commit_msg>Disable import of old users ranks.<commit_after>var RankController = function(configSource) {
    this._configSource = configSource;
    this._sortedRanks = this._sortedRanks(this._configSource.getRanks());
    var Datastore = require('nedb');
    this._db = new Datastore({
        filename: 'ranks.db',
        autoload: true
    });
    this._db.ensureIndex({ fieldName: 'name', unique: true }, function (err) {
        if (err) {
            alert("Can't create indexes");
        }
    });

    //this._importUsers();
};

RankController.prototype.processMessage = function (message, options, callback) {
    var expGain;
    if (options.firstMessage) {
        expGain = this._configSource.getFirstMessageExperience();
    } else if (options.smileOnly) {
        expGain = this._configSource.getSmileExperience();
    } else {
        expGain = this._configSource.getMessageExperience();
    }
    this._db.update({ name: message.nickname }, { $inc: { exp: expGain } }, { upsert: true });
    this._db.find({ name: message.nickname }, function (err, users) {
        if (err || users === undefined || users.length < 1) {
            return;
        }
        var user = users[0];
        var rankChanged = this._updateRank(user);
        callback(rankChanged, user);
    }.bind(this));
};

RankController.prototype.getRankId = function (username, callback) {
    this._db.find({ name: username }, function (err, users) {
        if (err) {
            callback(undefined);
            return;
        }
        if (users.length > 0) {
            callback(users[0].rankId);
            return;
        }
        callback(undefined);
    });
};

RankController.prototype.getRankById = function(rankId) {
    return this._configSource.getRanks()[rankId];
};

RankController.prototype._configSource = null;
RankController.prototype._db = null;
RankController.prototype._sortedRanks = null;

RankController.prototype._updateRank = function (user) {
    var newRankId = this._getNextRank(user.rankId, user.exp);
    if (newRankId !== user.rankId) {
        user.rankId = newRankId;
        this._db.update({ name: user.name }, { $set: { rankId: user.rankId } }, {});
        return user.rankId !== this._configSource.getDefaultRankId();
    }
    return false;
};

RankController.prototype._sortedRanks = function(ranks) {
    var unsortedRanks = [];
    var rankIds = Object.keys(ranks);
    rankIds.forEach(function(rankId, index) {
        var rank = ranks[rankId];
        var rankCopy = {
            "exp": rank.exp,
            "icon": rank.icon,
            "title": rank.title
        };
        rankCopy.id = rankId;
        unsortedRanks.push(rankCopy);
    });
    var sortedRanks = unsortedRanks.sort(function(a, b) {
        return a.exp - b.exp;
    });
    return sortedRanks;
};

RankController.prototype._getNextRank = function (currentRankId, exp) {
    if (currentRankId !== undefined) {
        if (this._configSource.getRanks()[currentRankId].exp < 0) {
            return currentRankId;
        }
        if (this._configSource.getRanks()[currentRankId].exp > exp) {
            exp = this._configSource.getRanks()[currentRankId].exp;
        }
    }
    var iRank;
    for (iRank = 0; iRank < this._sortedRanks.length; ++iRank) {
        if (this._sortedRanks[iRank].exp > exp) {
            break;
        }
    }

    if (iRank > 0) {
        return this._sortedRanks[iRank - 1].id;
    } else {
        return undefined;
    }
};

RankController.prototype._importUsers = function () {
    var usersFileName = "ranks.json";
    var conversionFileName = "ranksToNewRanks.json";
    var fs = require("fs");
    if (!fs.existsSync(usersFileName)) {
        return;
    }
    if (!fs.existsSync(conversionFileName)) {
        return;
    }
    var usersData = fs.readFileSync(usersFileName);
    var importedUsers = JSON.parse(usersData);
    var conversionData = fs.readFileSync(conversionFileName);
    var rankToNewRank = JSON.parse(conversionData);

    var usersPerLevel = {};
    var maxExp = 0;
    var maxExpName = null;
    importedUsers.forEach( function( user )
    {
        usersPerLevel[user.level] = usersPerLevel[user.level] || 0;
        usersPerLevel[user.level]++;
        if (user.exp > maxExp) {
            maxExp = user.exp;
            maxExpName = user.name;
        }

        // Conversion
        user.exp *= 10;
        var name = user.name;
        user.name = user.name.replace(/[^A-Za-z 0-9 \.,\?""!@#\$%\^&\*\(\)-_=\+;:<>\/\\\|\}\{\[\]`~]*/g, '');
        user.name = user.name.trim();
        if (user.name.length == 0) {
            console.log(name);
            return;
        }

        var convertedRankId = rankToNewRank[user.level];
        var convertedRank = this._configSource.getRanks()[convertedRankId];
        var convertedExp = user.exp > convertedRank.exp ? convertedRank.exp : user.exp;

        var convertedUser = new User();
        convertedUser.name = user.name;
        convertedUser.rankId = convertedRankId;
        convertedUser.exp = convertedExp;

        this._db.update({ name: user.name }, convertedUser, { upsert: true }, function (err, numReplaced, upsert) {

        });
    }.bind(this));
    var ranksStats = {
        "usersCount": importedUsers.length,
        "hightestRank": {
            "name": maxExpName,
            "exp": maxExp
        },
        "usersPerLevel": usersPerLevel
    };
    console.log(JSON.stringify(ranksStats, null, 2));
};<|endoftext|>


<commit_before>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

//Handles Drive files and APIs.
zr.service('drive', ['config', '$modal', '$timeout', '$location', 'realtime', 'zrdb',

	function (config, $modal, $timeout, $location, realtime, zrdb) {

		gapi.client.setApiKey(config.simpleApiKey);

		//Renames the current file in Drive.
		this.renameFile = function(title, id, callback) {
			gapi.client.request({
				'path': '/drive/v2/files/' + id,
				'method': 'PUT',
				'params': {
					'fields': '',
					'key': config.apiKey
				},
				'body': JSON.stringify({
					title: title
				})
			}).execute(function(){
				fileMetadata[id] = null;
				callback();
			});
		};

		var fileMetadata = {};
		//Gets the metadata of a file in Drive.
		this.getFileMetadata = function(id, callback) {
			if(fileMetadata[id]) {
				callback(fileMetadata[id]);
				return;
			}
			else {
				gapi.client.request({
					'path': '/drive/v2/files/' + id,
					'method': 'GET'
				}).execute(function(data) {
					fileMetadata[id] = data;
					callback(data);
				});
			}
		};

		var me = null;
		this.getUser = function(callback) {
			gapi.auth.checkSessionState({
				'client_id': config.clientId,
				'session_state': null
			}, function(loggedOut) {
				if(loggedOut) {
					me = null;
					callback(null);
					return;
				}
				else if(me) {
					callback(me);
					return;
				}
				else {
					realtime.requireAuth().then(function() {
						gapi.client.request({
							'path': '/plus/v1/people/me',
							'method': 'GET'
						}).execute(function(data){
							me = data;
							callback(data);
						});
					});
				}
			});
		};

		//Opens new project modal.
		this.newProject = function(folder) {
			realtime.requireAuth().then(function () {
				$modal.open({
					templateUrl: '/partials/new-project-modal.html',
					controller: 'NewProjectModalController',
					resolve: {
						folder: function() { return folder; },
						gameResources: function() {
							return zrdb.getAllResources('game');
						}
					}
				});
			});
		};

		this.openProject = function() {
			realtime.requireAuth().then(function () {
				var pickerCallback = function(data) {
					if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
						var doc = data[google.picker.Response.DOCUMENTS][0];
						var id = doc[google.picker.Document.ID];
						$timeout(function() {
							$location.url('/ide/' + id + '/');
						});
					}
				};
				var showPicker = function() {
					var picker = new google.picker.PickerBuilder().
						enableFeature(google.picker.Feature.NAV_HIDDEN).
						hideTitleBar().
						setAppId(config.appId).
						setOAuthToken(gapi.auth.getToken().access_token).
						addView(new google.picker.DocsView().
							setParent('root').
							setMode(google.picker.DocsViewMode.LIST).
							setIncludeFolders(true).
							setMimeTypes('application/vnd.google-apps.drive-sdk.' + config.appId)).
						setCallback(pickerCallback).
						build();
					picker.setVisible(true);
				}
				if(!window.google || !window.google.picker) {
					gapi.load('picker', {"callback" : showPicker});
				}
				else {
					showPicker();
				}
			});
		};
	}]
);
<commit_msg>Logged in field<commit_after>/**
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

'use strict';

//Handles Drive files and APIs.
zr.service('drive', ['config', '$modal', '$timeout', '$location', 'realtime', 'zrdb',

	function (config, $modal, $timeout, $location, realtime, zrdb) {

		gapi.client.setApiKey(config.simpleApiKey);

		//Renames the current file in Drive.
		this.renameFile = function(title, id, callback) {
			gapi.client.request({
				'path': '/drive/v2/files/' + id,
				'method': 'PUT',
				'params': {
					'fields': '',
					'key': config.apiKey
				},
				'body': JSON.stringify({
					title: title
				})
			}).execute(function(){
				fileMetadata[id] = null;
				callback();
			});
		};

		var fileMetadata = {};
		//Gets the metadata of a file in Drive.
		this.getFileMetadata = function(id, callback) {
			if(fileMetadata[id]) {
				callback(fileMetadata[id]);
				return;
			}
			else {
				gapi.client.request({
					'path': '/drive/v2/files/' + id,
					'method': 'GET'
				}).execute(function(data) {
					fileMetadata[id] = data;
					callback(data);
				});
			}
		};

		var me = null;
		this.getUser = function(callback) {
			gapi.auth.checkSessionState({
				'client_id': config.clientId,
				'session_state': null
			}, function(loggedOut) {
				if(loggedOut) {
					me = null;
					callback(null);
					return;
				}
				else if(me) {
					callback(me);
					return;
				}
				else {
					realtime.requireAuth(true).then(function() {
						gapi.client.request({
							'path': '/plus/v1/people/me',
							'method': 'GET'
						}).execute(function(data){
							me = data;
							callback(data);
						});
					});
				}
			});
		};

		//Opens new project modal.
		this.newProject = function(folder) {
			realtime.requireAuth().then(function () {
				$modal.open({
					templateUrl: '/partials/new-project-modal.html',
					controller: 'NewProjectModalController',
					resolve: {
						folder: function() { return folder; },
						gameResources: function() {
							return zrdb.getAllResources('game');
						}
					}
				});
			});
		};

		this.openProject = function() {
			realtime.requireAuth().then(function () {
				var pickerCallback = function(data) {
					if (data[google.picker.Response.ACTION] == google.picker.Action.PICKED) {
						var doc = data[google.picker.Response.DOCUMENTS][0];
						var id = doc[google.picker.Document.ID];
						$timeout(function() {
							$location.url('/ide/' + id + '/');
						});
					}
				};
				var showPicker = function() {
					var picker = new google.picker.PickerBuilder().
						enableFeature(google.picker.Feature.NAV_HIDDEN).
						hideTitleBar().
						setAppId(config.appId).
						setOAuthToken(gapi.auth.getToken().access_token).
						addView(new google.picker.DocsView().
							setParent('root').
							setMode(google.picker.DocsViewMode.LIST).
							setIncludeFolders(true).
							setMimeTypes('application/vnd.google-apps.drive-sdk.' + config.appId)).
						setCallback(pickerCallback).
						build();
					picker.setVisible(true);
				}
				if(!window.google || !window.google.picker) {
					gapi.load('picker', {"callback" : showPicker});
				}
				else {
					showPicker();
				}
			});
		};
	}]
);
<|endoftext|>


<commit_before>window.beURL = "http://212.47.250.190:3000"
<commit_msg>set env var for front end<commit_after>window.beURL = "http://212.47.250.190:3000";
window.env = "production";
<|endoftext|>