<commit_before>/**
 * Created by hpb on 7/15/2017.
 */
$(document).ready(function() {
    $('#example1').DataTable({responsive: true});

} );

$(document).on("click", ".open-modal-action", function () {
    var item_id = $(this).data('id');
    $(".modal-body #item-id").val( item_id );

    var item = $(this).data('id');
    $(".modal-body #dist-id").val( item );

    var return_id = $(this).data('id');
    $(".modal-body #return-id").val( return_id );

    var user_id = $(this).data('id');
    $(".modal-body #user-id").val( user_id );
});

var inventory;
var department;
var increaselog;

$(document).ready(function() {

    inventory = $('#table').DataTable({

        destroy: true,
        responsive: true,
        "ajax": {
            "url": 'inventory/inventory_list',
            "type": "POST",
        },
    });

    department = $('#department').DataTable({
        destroy: true,
        responsive: true,
        "ajax": {
            "url":'department/get_all_dept_list',
            "type": "POST",
        },
    });

    $('select[name="department"]').change(function(){
        var id = $(this).val();

        department = $('#department').DataTable({
            responsive: true,
            "destroy": true,
            "ajax": {
                "url":'department/get_dept_list/'+id,
                "type": "POST",
            },
        });
    });

    increaselog = $('#increase_log').DataTable({
        destroy: true,
        responsive: true,
        "ajax": {
            "url": 'increaselog/increase_log_list',
            "type": "POST",
        },
    });

    decreaselog = $('#decrease_log').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'decreaselog/decrease_log_list',
            "type": "POST",
        },
    });

    returnlog = $('#return_log').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'returnlog/return_log_list',
            "type": "POST",
        },
    });

    returned_items = $('#returned_items').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'returned/returned_list',
            "type": "POST",
        },
    });

    pending = $('#pending').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'users/display_pending_users',
            "type": "POST",
        },
    });

    setInterval(function () {
        inventory.ajax.reload(null,false);
        department.ajax.reload(null,false);
        returned_items.ajax.reload(null,false);
        pending.ajax.reload(null,false);
    }, 1000);
});

function summary() {
    $('#departmentsummary').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'department/summary_items',
            "type": "POST",
        },
    });
    $('#summary').modal('show');
}

function subtract_quantity(id) {
    $.ajax({
        url: 'inventory/get_quantity/' + id,
        type:"POST",
        dataType: 'json',
        success: function(data){
            $('input[name="Quantity"]').attr('max', data.data);
            $('#subqty').modal('show');
        },
    });

}

function return_no_action(id) {
    $.ajax({
        url: 'returned/no_action/' + id,
        type:"POST",
        success: function() {
            location.href = window.location.origin + "/app/returned"
        }
    });

}

function get_item_details(id) {
    var serial;
    var oldData;
    details = $('#details').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'inventory/itemdetail/' + id,
            "type": "GET",
        },
    });
   $('#details').on( 'click', 'tr :nth-child(2)', function () {
       $(this).attr('contentEditable', 'true');
       $(this).focus(function () {
           oldData = $(this).text();
       });
       $(this).blur(function () {
            serial = $(this).text();
           if(oldData != serial) {
               $.ajax({
                   type: "POST",
                   url: 'inventory/set_serial/' + id,
                   data: {'serial':serial},
                   dataType: 'json',
               });
           }
       });

    });

    $('#view').modal('show');
}

function get_distribution_details(id) {
    $('#details').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'department/dist_details/' + id,
            "type": "POST",
        },
    });
    $('#view').modal('show');
}

var save_method; //for save method string

function edit_inventory(id)
{
    save_method = 'update';

    //Ajax Load data from ajax
    $.ajax({
        url : 'inventory/edit/' + id,
        type: "GET",
        dataType: "JSON",
        success: function(data)
        {
            $.each(data, function(i, item) {
                $('[name="item_id"]').val(data[i].item_id);
                $('[name="item_name"]').val(data[i].item_name);
                $('[name="desc"]').val(data[i].item_description);
                $('[name="unit"]').val(data[i].unit);
                $('[name="qty"]').val(data[i].quantity);
            });
            $('#edit').modal('show'); // show bootstrap modal when complete loaded

        },
        error: function (jqXHR, textStatus, errorThrown)
        {
            alert('Error get data from ajax');
        }
    });
}
function save()
{
    var url;
    if(save_method === 'update')

        url = 'admin/edit/item_update';

    // ajax adding data to database
    $.ajax({
        url : url,
        type: "POST",
        data: $('#form').serialize(),
        dataType: "JSON",
        success: function(data)
        {
            //if success close modal and reload ajax table
            $('#edit').modal('hide');
            location.reload();// for reload a page
        },
        error: function (jqXHR, textStatus, errorThrown)
        {
            alert('Error adding / update data');
        }
    });
}
function create_chart()
{
    var chart;
    // Build the chart
    $('#items').highcharts({
        chart: {
            plotBackgroundColor: null,
            plotBorderWidth: null,
            plotShadow: false
        },
        title: {
            text: 'Items'
        },
        tooltip: {
            pointFormat: '{series.name}: {point.percentage:.1f}%'
        },
        plotOptions: {
            pie: {
                allowPointSelect: true,
                cursor: 'pointer',
                dataLabels: {
                    enabled: false
                },
                showInLegend: true
            }
        },
        series: [{
            type: 'pie',
            name: 'Top 5 items',
            data: [
                ['Ballpen',   4],
                ['Table',       2],
                ['Bag',    1],
                ['Paper',     1],
                ['Laptop',   2]
            ]
        }]
    });
}

//form validations
    $(document).ready(function () {
        $( "input[name=Item_Quantity]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 || x === 46 || x === 45 || x === 43){
                return false;
            }
        });
        $( "input[name=Cost]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 ||  x === 45 || x === 43){
                return false;
            }
        });
        $( "input[id=new_qty]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 || x === 46 || x === 45 || x === 43){
                return false;
            }
        });
        $( "input[id=new_cost]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 ||  x === 45 || x === 43){
                return false;
            }
        });
        $( "input[name=datereceived]" ).on("blur",function () {
            del_date = new Date($('input[name=datedelivered]').val());
            date_rec = new Date($('input[name=datereceived]').val());

            if(del_date > date_rec){
                $(this).val('');
            }
        });

        $( "input[name=ExpirationDate]" ).on("blur",function () {
            date_rec = new Date($('input[name=datereceived]').val());
            exp_date = new Date($('input[name=ExpirationDate]').val());

            if(exp_date < date_rec){
                $(this).val('');
            }
        });

    });
//return items

function return_selected_items() {
    var item_det_id = [];
    var reason;
    var person;
    $('#item_detail:checked').each(function () {
        item_det_id.push($(this).val());
    });
    $('#returnmodal').modal('show');
   $('button[id=save1]').on('click',function () {
        reason =  $('input[name=reason]').val();
        person =  $('input[name=person]').val();
        var item_data = {'item_det_id':item_det_id,'reason':reason,'person':person};
       $.ajax({
           url : 'department/return_items',
           type: "POST",
           data: item_data,
           dataType: "JSON",
       });
   });
}

$(document).ready(function () {
    $('input[name=select-all]').change(function () {
        $('input[name=item-det]').prop('checked',this.checked);
    });
});<commit_msg>cursor problem<commit_after>/**
 * Created by hpb on 7/15/2017.
 */
$(document).ready(function() {
    $('#example1').DataTable({responsive: true});

} );

$(document).on("click", ".open-modal-action", function () {
    var item_id = $(this).data('id');
    $(".modal-body #item-id").val( item_id );

    var item = $(this).data('id');
    $(".modal-body #dist-id").val( item );

    var return_id = $(this).data('id');
    $(".modal-body #return-id").val( return_id );

    var user_id = $(this).data('id');
    $(".modal-body #user-id").val( user_id );
});

var inventory;
var department;
var increaselog;

$(document).ready(function() {

    inventory = $('#table').DataTable({

        destroy: true,
        responsive: true,
        "ajax": {
            "url": 'inventory/inventory_list',
            "type": "POST",
        },
    });

    department = $('#department').DataTable({
        destroy: true,
        responsive: true,
        "ajax": {
            "url":'department/get_all_dept_list',
            "type": "POST",
        },
    });

    $('select[name="department"]').change(function(){
        var id = $(this).val();

        department = $('#department').DataTable({
            responsive: true,
            "destroy": true,
            "ajax": {
                "url":'department/get_dept_list/'+id,
                "type": "POST",
            },
        });
    });

    increaselog = $('#increase_log').DataTable({
        destroy: true,
        responsive: true,
        "ajax": {
            "url": 'increaselog/increase_log_list',
            "type": "POST",
        },
    });

    decreaselog = $('#decrease_log').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'decreaselog/decrease_log_list',
            "type": "POST",
        },
    });

    returnlog = $('#return_log').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'returnlog/return_log_list',
            "type": "POST",
        },
    });

    returned_items = $('#returned_items').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'returned/returned_list',
            "type": "POST",
        },
    });

    pending = $('#pending').DataTable({
        responsive: true,
        "destroy":true,
        "ajax": {
            "url": 'users/display_pending_users',
            "type": "POST",
        },
    });

    setInterval(function () {
        inventory.ajax.reload(null,false);
        department.ajax.reload(null,false);
        returned_items.ajax.reload(null,false);
        pending.ajax.reload(null,false);
    }, 1000);
});

function summary() {
    $('#departmentsummary').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'department/summary_items',
            "type": "POST",
        },
    });
    $('#summary').modal('show');
}

function subtract_quantity(id) {
    $.ajax({
        url: 'inventory/get_quantity/' + id,
        type:"POST",
        dataType: 'json',
        success: function(data){
            $('input[name="Quantity"]').attr('max', data.data);
            $('#subqty').modal('show');
        },
    });

}

function return_no_action(id) {
    $.ajax({
        url: 'returned/no_action/' + id,
        type:"POST",
        success: function() {
            location.href = window.location.origin + "/app/returned"
        }
    });

}

function get_item_details(id) {
    var serial;
    var oldData;
    details = $('#details').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'inventory/itemdetail/' + id,
            "type": "GET",
        },
    });
   $('#details').on( 'click', 'tr :nth-child(2)', function () {
       $(this).attr('contentEditable', 'true');
       $(this).focus(function () {
           oldData = $(this).text();
       });
       $('tbody > tr :nth-child(2)').on('blur keyup change', function () {
           serial = $(this).text();
           $('tbody > tr :nth-child(2)').text(serial); // kaya pumupunta ung cursor sa umpisa kasi nauupdate din ung  - var serial
       });
      // $(this).blur(function () {
       //     serial = $(this).text();
           if(oldData != serial) {
               $.ajax({
                   type: "POST",
                   url: 'inventory/set_serial/' + id,
                   data: {'serial':serial},
                   dataType: 'json',
               });
           }
       });

   // });

    $('#view').modal('show');
}

function get_distribution_details(id) {
    $('#details').DataTable({
        responsive: true,
        "destroy": true,
        "ajax": {
            "url": 'department/dist_details/' + id,
            "type": "POST",
        },
    });
    $('#view').modal('show');
}

var save_method; //for save method string

function edit_inventory(id)
{
    save_method = 'update';

    //Ajax Load data from ajax
    $.ajax({
        url : 'inventory/edit/' + id,
        type: "GET",
        dataType: "JSON",
        success: function(data)
        {
            $.each(data, function(i, item) {
                $('[name="item_id"]').val(data[i].item_id);
                $('[name="item_name"]').val(data[i].item_name);
                $('[name="desc"]').val(data[i].item_description);
                $('[name="unit"]').val(data[i].unit);
                $('[name="qty"]').val(data[i].quantity);
            });
            $('#edit').modal('show'); // show bootstrap modal when complete loaded

        },
        error: function (jqXHR, textStatus, errorThrown)
        {
            alert('Error get data from ajax');
        }
    });
}
function save()
{
    var url;
    if(save_method === 'update')

        url = 'admin/edit/item_update';

    // ajax adding data to database
    $.ajax({
        url : url,
        type: "POST",
        data: $('#form').serialize(),
        dataType: "JSON",
        success: function(data)
        {
            //if success close modal and reload ajax table
            $('#edit').modal('hide');
            location.reload();// for reload a page
        },
        error: function (jqXHR, textStatus, errorThrown)
        {
            alert('Error adding / update data');
        }
    });
}
function create_chart()
{
    var chart;
    // Build the chart
    $('#items').highcharts({
        chart: {
            plotBackgroundColor: null,
            plotBorderWidth: null,
            plotShadow: false
        },
        title: {
            text: 'Items'
        },
        tooltip: {
            pointFormat: '{series.name}: {point.percentage:.1f}%'
        },
        plotOptions: {
            pie: {
                allowPointSelect: true,
                cursor: 'pointer',
                dataLabels: {
                    enabled: false
                },
                showInLegend: true
            }
        },
        series: [{
            type: 'pie',
            name: 'Top 5 items',
            data: [
                ['Ballpen',   4],
                ['Table',       2],
                ['Bag',    1],
                ['Paper',     1],
                ['Laptop',   2]
            ]
        }]
    });
}

//form validations
    $(document).ready(function () {
        $( "input[name=Item_Quantity]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 || x === 46 || x === 45 || x === 43){
                return false;
            }
        });
        $( "input[name=Cost]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 ||  x === 45 || x === 43){
                return false;
            }
        });
        $( "input[id=new_qty]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 || x === 46 || x === 45 || x === 43){
                return false;
            }
        });
        $( "input[id=new_cost]" ).on("keypress paste",function () {
            var x = event.charCode;
            if(x === 101 ||  x === 45 || x === 43){
                return false;
            }
        });
        $( "input[name=datereceived]" ).on("blur",function () {
            del_date = new Date($('input[name=datedelivered]').val());
            date_rec = new Date($('input[name=datereceived]').val());

            if(del_date > date_rec){
                $(this).val('');
            }
        });

        $( "input[name=ExpirationDate]" ).on("blur",function () {
            date_rec = new Date($('input[name=datereceived]').val());
            exp_date = new Date($('input[name=ExpirationDate]').val());

            if(exp_date < date_rec){
                $(this).val('');
            }
        });

    });
//return items

function return_selected_items() {
    var item_det_id = [];
    var reason;
    var person;
    $('#item_detail:checked').each(function () {
        item_det_id.push($(this).val());
    });
    $('#returnmodal').modal('show');
   $('button[id=save1]').on('click',function () {
        reason =  $('input[name=reason]').val();
        person =  $('input[name=person]').val();
        var item_data = {'item_det_id':item_det_id,'reason':reason,'person':person};
       $.ajax({
           url : 'department/return_items',
           type: "POST",
           data: item_data,
           dataType: "JSON",
       });
   });
}

$(document).ready(function () {
    $('input[name=select-all]').change(function () {
        $('input[name=item-det]').prop('checked',this.checked);
    });
});<|endoftext|>


<commit_before>// Ember!
import Ember from 'ember';

// Merge method
const { merge } = Ember;

/**
 * Default route for product search,
 * its based on the YeboSDK Query system
 * @todo Define some outlets like 'aggs', 'pagination', 'sort',
 *       to share more components in the search pages
 */
export default Ember.Mixin.create({
  /**
   * Default params
   * @todo Enable the user to add new actions without overwriting this
   */
  queryParams: {
    search: {
      refreshModel: true
    },
    page: {
      refreshModel: true
    },
    sort: {
      refreshModel: true
    }
  },

  /**
   * Default Sort
   */
  defaultSort: 'price-desc',

  /**
   * Products per page
   */
  perPage: 15,

  /**
   * Current aggregation
   */
  currentAggregation: null,

  /**
   * Selected aggregations used to generate
   * the next query
   */
  selectedAggs: {},

  /**
   * Flag to check if its necessary to fetch
   * aggregations again
   */
  refreshAggregations: true,

  /**
   * Route Model
   */
  model: function(params) {
    // Create a new query
    let query = new YeboSDK.Products();

    // Define the number of results per page
    query.perPage(this.get('perPage'));

    // Set the search
    if( params.search !== undefined )
      query.search(params.search);

    // Define the sort param
    let sortParam;

    // Check if the sort param exists
    if( params.sort !== undefined )
      sortParam = params.sort;
    else
      sortParam = this.get('defaultSort');

    // Sort options
    let sortOptions = sortParam.split('-');

    // Set the page
    query.sortBy(sortOptions[0], sortOptions[1]);

    // Set the page
    query.page(params.page);

    // Define the search rules
    this.searchRules(query, params, this.get('selectedAggs'));

    // Set the default promises
    let defaultPromises = {
      search: this.get('yebo.products').search(query),
      taxonomies: this.yebo.store.findAll('taxonomy'),
      selectedAggs: this.get('selectedAggs')
    };

    // Check if its necessary to get the aggregations
    if( this.get('currentAggregation') === null || this.get('refreshAggregations') )
      defaultPromises.aggs = query.aggregations();
    else
      defaultPromises.aggs = this.get('currentAggregation');

    // Return a promise to the route
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Execute the default model promises
      Ember.RSVP.hash(defaultPromises).then((result) => {
        // Set the current aggregation
        this.set('currentAggregation', result.aggs);

        // Define the route model
        let routeModel = this.searchModel(params);

        // Check if it is a promise
        if( routeModel.then === undefined )
          return resolve(merge(routeModel, result));

        // Resolve the promise with the result
        routeModel.then((res) => { resolve(merge(res, result)); });
      });
    });
  },

  /**
   * Clear the query params when the user leaves the search page
   */
  resetController(controller, isExiting, transition) {
    // Check if the user is leaving the page
    if (isExiting) {
      // Reset the page
      controller.set('page', 1);

      // Reset the search
      controller.set('search', undefined);

      // Reset the sort
      controller.set('sort', undefined);
    }
  },

  /**
   * Route actions
   * @todo Enable the user to add new actions without overwriting this
   */
  actions: {
    changePage: function(pageNumber) {
      // Change the page number
      this.transitionTo({ queryParams: { page: pageNumber } })
    },
    changeSort: function(sort) {
      // Change the sort
      this.transitionTo({ queryParams: { sort: sort } })
    }
  },

  /**
   * Generate the search rules and put it into the
   * query that is going to be used
   * @todo Enable this method to be async
   * @param {YeboSDK.Products} query The query that is being formed,
   *                           so new rules will be added into this new query.
   * @param {Object} params Parameters that come from the route
   * @param {Object} aggs The selected aggregations
   * @return {void} This method does not return anything
   */
  searchRules(query, params, aggs) {
    // Generated rules
    let rules = [];

    // Set the price filter
    if( aggs.price !== undefined )
      rules.push(new YeboSDK.Products.Rules.price(aggs.price.split('-')));

    // Set filters
    if( aggs.filters !== undefined ) {
      // Each the filters
      for( let filter in aggs.filters ) {
        // Get the filter values
        let values = aggs.filters[filter];

        // Add the rule
        rules.push(new YeboSDK.Products.Rules.filter(filter, values));
      }
    }

    // As default set the relation
    // between the aggregations as `and`
    query.and(rules);
  },

  /**
   * This method enable the route to add info into the
   * model object that is returned
   * @param {Object} params Parameters that come from the route
   * @return {Object} This object will be merged into the default model
   *                  from the search. It can be a Promise that
   *                  returns an Object.
   */
  searchModel(params) {
    // As default return an empty object
    return {};
  }
});
<commit_msg>Set default price ranges (for aggregations)<commit_after>// Ember!
import Ember from 'ember';

// Merge method
const { merge } = Ember;

/**
 * Default route for product search,
 * its based on the YeboSDK Query system
 * @todo Define some outlets like 'aggs', 'pagination', 'sort',
 *       to share more components in the search pages
 */
export default Ember.Mixin.create({
  /**
   * Default params
   * @todo Enable the user to add new actions without overwriting this
   */
  queryParams: {
    search: {
      refreshModel: true
    },
    page: {
      refreshModel: true
    },
    sort: {
      refreshModel: true
    }
  },

  /**
   * Default Sort
   */
  defaultSort: 'price-desc',

  /**
   * Products per page
   */
  perPage: 15,

  /**
   * Default price ranges
   */
  defaultPriceRanges: [
    { to: 49.99 },
    { from: 50, to: 99.99 },
    { from: 100 }
  ],

  /**
   * Current aggregation
   */
  currentAggregation: null,

  /**
   * Selected aggregations used to generate
   * the next query
   */
  selectedAggs: {},

  /**
   * Flag to check if its necessary to fetch
   * aggregations again
   */
  refreshAggregations: true,

  /**
   * Route Model
   */
  model: function(params) {
    // Create a new query
    let query = new YeboSDK.Products();

    // Define the number of results per page
    query.perPage(this.get('perPage'));

    // Set the search
    if( params.search !== undefined )
      query.search(params.search);

    // Define the sort param
    let sortParam;

    // Check if the sort param exists
    if( params.sort !== undefined )
      sortParam = params.sort;
    else
      sortParam = this.get('defaultSort');

    // Sort options
    let sortOptions = sortParam.split('-');

    // Set the page
    query.sortBy(sortOptions[0], sortOptions[1]);

    // Set the page
    query.page(params.page);

    // Define the search rules
    this.searchRules(query, params, this.get('selectedAggs'));

    // Set the default promises
    let defaultPromises = {
      search: this.get('yebo.products').search(query),
      taxonomies: this.yebo.store.findAll('taxonomy'),
      selectedAggs: this.get('selectedAggs')
    };

    // Check if its necessary to get the aggregations
    if( this.get('currentAggregation') === null || this.get('refreshAggregations') )
      defaultPromises.aggs = query.aggregations(undefined, this.get('defaultPriceRanges'));
    else
      defaultPromises.aggs = this.get('currentAggregation');

    // Return a promise to the route
    return new Ember.RSVP.Promise((resolve, reject) => {
      // Execute the default model promises
      Ember.RSVP.hash(defaultPromises).then((result) => {
        // Set the current aggregation
        this.set('currentAggregation', result.aggs);

        // Define the route model
        let routeModel = this.searchModel(params);

        // Check if it is a promise
        if( routeModel.then === undefined )
          return resolve(merge(routeModel, result));

        // Resolve the promise with the result
        routeModel.then((res) => { resolve(merge(res, result)); });
      });
    });
  },

  /**
   * Clear the query params when the user leaves the search page
   */
  resetController(controller, isExiting, transition) {
    // Check if the user is leaving the page
    if (isExiting) {
      // Reset the page
      controller.set('page', 1);

      // Reset the search
      controller.set('search', undefined);

      // Reset the sort
      controller.set('sort', undefined);
    }
  },

  /**
   * Route actions
   * @todo Enable the user to add new actions without overwriting this
   */
  actions: {
    changePage: function(pageNumber) {
      // Change the page number
      this.transitionTo({ queryParams: { page: pageNumber } })
    },
    changeSort: function(sort) {
      // Change the sort
      this.transitionTo({ queryParams: { sort: sort } })
    }
  },

  /**
   * Generate the search rules and put it into the
   * query that is going to be used
   * @todo Enable this method to be async
   * @param {YeboSDK.Products} query The query that is being formed,
   *                           so new rules will be added into this new query.
   * @param {Object} params Parameters that come from the route
   * @param {Object} aggs The selected aggregations
   * @return {void} This method does not return anything
   */
  searchRules(query, params, aggs) {
    // Generated rules
    let rules = [];

    // Set the price filter
    if( aggs.price !== undefined )
      rules.push(new YeboSDK.Products.Rules.price(aggs.price.split('-')));

    // Set filters
    if( aggs.filters !== undefined ) {
      // Each the filters
      for( let filter in aggs.filters ) {
        // Get the filter values
        let values = aggs.filters[filter];

        // Add the rule
        rules.push(new YeboSDK.Products.Rules.filter(filter, values));
      }
    }

    // As default set the relation
    // between the aggregations as `and`
    query.and(rules);
  },

  /**
   * This method enable the route to add info into the
   * model object that is returned
   * @param {Object} params Parameters that come from the route
   * @return {Object} This object will be merged into the default model
   *                  from the search. It can be a Promise that
   *                  returns an Object.
   */
  searchModel(params) {
    // As default return an empty object
    return {};
  }
});
<|endoftext|>


<commit_before>/**
 * Blueprint API Configuration
 * (sails.config.blueprints)
 *
 * These settings are for the global configuration of blueprint routes and
 * request options (which impact the behavior of blueprint actions).
 *
 * You may also override any of these settings on a per-controller basis
 * by defining a '_config' key in your controller definition, and assigning it
 * a configuration object with overrides for the settings in this file.
 * A lot of the configuration options below affect so-called "CRUD methods",
 * or your controllers' `find`, `create`, `update`, and `destroy` actions.
 *
 * It's important to realize that, even if you haven't defined these yourself, as long as
 * a model exists with the same name as the controller, Sails will respond with built-in CRUD
 * logic in the form of a JSON API, including support for sort, pagination, and filtering.
 *
 * For more information on the blueprint API, check out:
 * http://sailsjs.org/#!/documentation/reference/blueprint-api
 *
 * For more information on the settings in this file, see:
 * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.blueprints.html
 *
 */

module.exports.blueprints = {

  /***************************************************************************
  *                                                                          *
  * Action routes speed up the backend development workflow by               *
  * eliminating the need to manually bind routes. When enabled, GET, POST,   *
  * PUT, and DELETE routes will be generated for every one of a controller's *
  * actions.                                                                 *
  *                                                                          *
  * If an `index` action exists, additional naked routes will be created for *
  * it. Finally, all `actions` blueprints support an optional path           *
  * parameter, `id`, for convenience.                                        *
  *                                                                          *
  * `actions` are enabled by default, and can be OK for production--         *
  * however, if you'd like to continue to use controller/action autorouting  *
  * in a production deployment, you must take great care not to              *
  * inadvertently expose unsafe/unintentional controller logic to GET        *
  * requests.                                                                *
  *                                                                          *
  ***************************************************************************/

  actions: false,

  /***************************************************************************
  *                                                                          *
  * RESTful routes (`sails.config.blueprints.rest`)                          *
  *                                                                          *
  * REST blueprints are the automatically generated routes Sails uses to     *
  * expose a conventional REST API on top of a controller's `find`,          *
  * `create`, `update`, and `destroy` actions.                               *
  *                                                                          *
  * For example, a BoatController with `rest` enabled generates the          *
  * following routes:                                                        *
  * :::::::::::::::::::::::::::::::::::::::::::::::::::::::                  *
  *  GET /boat -> BoatController.find                                        *
  *  GET /boat/:id -> BoatController.findOne                                 *
  *  POST /boat -> BoatController.create                                     *
  *  PUT /boat/:id -> BoatController.update                                  *
  *  DELETE /boat/:id -> BoatController.destroy                              *
  *                                                                          *
  * `rest` blueprint routes are enabled by default, and are suitable for use *
  * in a production scenario, as long you take standard security precautions *
  * (combine w/ policies, etc.)                                              *
  *                                                                          *
  ***************************************************************************/

  rest: true,

  /***************************************************************************
  *                                                                          *
  * Shortcut routes are simple helpers to provide access to a                *
  * controller's CRUD methods from your browser's URL bar. When enabled,     *
  * GET, POST, PUT, and DELETE routes will be generated for the              *
  * controller's`find`, `create`, `update`, and `destroy` actions.           *
  *                                                                          *
  * `shortcuts` are enabled by default, but should be disabled in            *
  * production.                                                              *
  *                                                                          *
  ***************************************************************************/

  shortcuts: true,

  /***************************************************************************
  *                                                                          *
  * An optional mount path for all blueprint routes on a controller,         *
  * including `rest`, `actions`, and `shortcuts`. This allows you to take    *
  * advantage of blueprint routing, even if you need to namespace your API   *
  * methods.                                                                 *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  ***************************************************************************/

  prefix: '',

  /***************************************************************************
   *                                                                          *
   * An optional mount path for all REST blueprint routes on a controller.    *
   * And it do not include `actions` and `shortcuts` routes.                  *
   * This allows you to take advantage of REST blueprint routing,             *
   * even if you need to namespace your RESTful API methods                   *
   *                                                                          *
   ***************************************************************************/

  restPrefix: '',

  /***************************************************************************
  *                                                                          *
  * Whether to pluralize controller names in blueprint routes.               *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  * For example, REST blueprints for `FooController` with `pluralize`        *
  * enabled:                                                                 *
  * GET /foos/:id?                                                           *
  * POST /foos                                                               *
  * PUT /foos/:id?                                                           *
  * DELETE /foos/:id?                                                        *
  *                                                                          *
  ***************************************************************************/

  pluralize: false,

  /***************************************************************************
  *                                                                          *
  * Whether the blueprint controllers should populate model fetches with     *
  * data from other models which are linked by associations                  *
  *                                                                          *
  * If you have a lot of data in one-to-many associations, leaving this on   *
  * may result in very heavy api calls                                       *
  *                                                                          *
  ***************************************************************************/

  populate: false,

  /****************************************************************************
  *                                                                           *
  * Whether to run Model.watch() in the find and findOne blueprint actions.   *
  * Can be overridden on a per-model basis.                                   *
  *                                                                           *
  ****************************************************************************/

  autoWatch: false,

  index: false,

  /****************************************************************************
  *                                                                           *
  * The default number of records to show in the response from a "find"       *
  * action. Doubles as the default size of populated arrays if populate is    *
  * true.                                                                     *
  *                                                                           *
  ****************************************************************************/

  defaultLimit: 30

};
<commit_msg>fixed blueprints settings<commit_after>/**
 * Blueprint API Configuration
 * (sails.config.blueprints)
 *
 * These settings are for the global configuration of blueprint routes and
 * request options (which impact the behavior of blueprint actions).
 *
 * You may also override any of these settings on a per-controller basis
 * by defining a '_config' key in your controller definition, and assigning it
 * a configuration object with overrides for the settings in this file.
 * A lot of the configuration options below affect so-called "CRUD methods",
 * or your controllers' `find`, `create`, `update`, and `destroy` actions.
 *
 * It's important to realize that, even if you haven't defined these yourself, as long as
 * a model exists with the same name as the controller, Sails will respond with built-in CRUD
 * logic in the form of a JSON API, including support for sort, pagination, and filtering.
 *
 * For more information on the blueprint API, check out:
 * http://sailsjs.org/#!/documentation/reference/blueprint-api
 *
 * For more information on the settings in this file, see:
 * http://sailsjs.org/#!/documentation/reference/sails.config/sails.config.blueprints.html
 *
 */

module.exports.blueprints = {

  /***************************************************************************
  *                                                                          *
  * Action routes speed up the backend development workflow by               *
  * eliminating the need to manually bind routes. When enabled, GET, POST,   *
  * PUT, and DELETE routes will be generated for every one of a controller's *
  * actions.                                                                 *
  *                                                                          *
  * If an `index` action exists, additional naked routes will be created for *
  * it. Finally, all `actions` blueprints support an optional path           *
  * parameter, `id`, for convenience.                                        *
  *                                                                          *
  * `actions` are enabled by default, and can be OK for production--         *
  * however, if you'd like to continue to use controller/action autorouting  *
  * in a production deployment, you must take great care not to              *
  * inadvertently expose unsafe/unintentional controller logic to GET        *
  * requests.                                                                *
  *                                                                          *
  ***************************************************************************/

  actions: false,

  /***************************************************************************
  *                                                                          *
  * RESTful routes (`sails.config.blueprints.rest`)                          *
  *                                                                          *
  * REST blueprints are the automatically generated routes Sails uses to     *
  * expose a conventional REST API on top of a controller's `find`,          *
  * `create`, `update`, and `destroy` actions.                               *
  *                                                                          *
  * For example, a BoatController with `rest` enabled generates the          *
  * following routes:                                                        *
  * :::::::::::::::::::::::::::::::::::::::::::::::::::::::                  *
  *  GET /boat -> BoatController.find                                        *
  *  GET /boat/:id -> BoatController.findOne                                 *
  *  POST /boat -> BoatController.create                                     *
  *  PUT /boat/:id -> BoatController.update                                  *
  *  DELETE /boat/:id -> BoatController.destroy                              *
  *                                                                          *
  * `rest` blueprint routes are enabled by default, and are suitable for use *
  * in a production scenario, as long you take standard security precautions *
  * (combine w/ policies, etc.)                                              *
  *                                                                          *
  ***************************************************************************/

  rest: false,

  /***************************************************************************
  *                                                                          *
  * Shortcut routes are simple helpers to provide access to a                *
  * controller's CRUD methods from your browser's URL bar. When enabled,     *
  * GET, POST, PUT, and DELETE routes will be generated for the              *
  * controller's`find`, `create`, `update`, and `destroy` actions.           *
  *                                                                          *
  * `shortcuts` are enabled by default, but should be disabled in            *
  * production.                                                              *
  *                                                                          *
  ***************************************************************************/

  shortcuts: false,

  /***************************************************************************
  *                                                                          *
  * An optional mount path for all blueprint routes on a controller,         *
  * including `rest`, `actions`, and `shortcuts`. This allows you to take    *
  * advantage of blueprint routing, even if you need to namespace your API   *
  * methods.                                                                 *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  ***************************************************************************/

  prefix: "",

  /***************************************************************************
   *                                                                          *
   * An optional mount path for all REST blueprint routes on a controller.    *
   * And it do not include `actions` and `shortcuts` routes.                  *
   * This allows you to take advantage of REST blueprint routing,             *
   * even if you need to namespace your RESTful API methods                   *
   *                                                                          *
   ***************************************************************************/

  restPrefix: "",

  /***************************************************************************
  *                                                                          *
  * Whether to pluralize controller names in blueprint routes.               *
  *                                                                          *
  * (NOTE: This only applies to blueprint autoroutes, not manual routes from *
  * `sails.config.routes`)                                                   *
  *                                                                          *
  * For example, REST blueprints for `FooController` with `pluralize`        *
  * enabled:                                                                 *
  * GET /foos/:id?                                                           *
  * POST /foos                                                               *
  * PUT /foos/:id?                                                           *
  * DELETE /foos/:id?                                                        *
  *                                                                          *
  ***************************************************************************/

  pluralize: true,

  /***************************************************************************
  *                                                                          *
  * Whether the blueprint controllers should populate model fetches with     *
  * data from other models which are linked by associations                  *
  *                                                                          *
  * If you have a lot of data in one-to-many associations, leaving this on   *
  * may result in very heavy api calls                                       *
  *                                                                          *
  ***************************************************************************/

  populate: false,

  /****************************************************************************
  *                                                                           *
  * Whether to run Model.watch() in the find and findOne blueprint actions.   *
  * Can be overridden on a per-model basis.                                   *
  *                                                                           *
  ****************************************************************************/

  autoWatch: false,

  /****************************************************************************
  *                                                                           *
  * The default number of records to show in the response from a "find"       *
  * action. Doubles as the default size of populated arrays if populate is    *
  * true.                                                                     *
  *                                                                           *
  ****************************************************************************/

  defaultLimit: 30

};
<|endoftext|>


<commit_before>;(function()
{
	/**
	 * @namespace
	 * @description
Functions for loading scripts asynchronously,
	 * detecting platform, and other common application functionality.

	 * @alias evothings
	 * @public
	 */
	var evothings = window.evothings || {};
	window.evothings = evothings;

	/* ------------------ Script loading ------------------ */

	var scriptLoadingCounter = 0;
	var loadedScripts = {};
	var scriptsLoadedCallbacks = [];

	/* Make sure to catch any DOMContentLoaded events occurring before
	 * asynchronous loading of scripts. Those scripts, like ui.js, should check
	 * this variable before listening for the event. */
	evothings.gotDOMContentLoaded = false

	window.addEventListener('DOMContentLoaded', function(e)
	{
		evothings.gotDOMContentLoaded = true
	})

	/**
	 * Load a script.
	 * @param {string} url - URL or path to the script. Relative paths are
	 * relative to the HTML file that initiated script loading.
	 * @param {function} callback - Optional parameterless function that will
	 * be called when the script has loaded.
	 * @public
	 */
	evothings.loadScript = function(url, callback)
	{
		// If script is already loaded call callback directly and return.
		if (loadedScripts[url])
		{
			callback && callback();
			return;
		}

		// Add script to dictionary of loaded scripts.
		loadedScripts[url] = 'loadingstarted';
		++scriptLoadingCounter;

		// Create script tag.
		var script = document.createElement('script');
		script.type = 'text/javascript';
		script.src = url;

		// Bind the onload event.
		script.onload = function()
		{
			// Mark as loaded.
			loadedScripts[url] = 'loadingcomplete';
			--scriptLoadingCounter;

			// Call callback if given.
			callback && callback();

			// Call scripts loaded callbacks if this was the last script loaded.
			if (0 == scriptLoadingCounter)
			{
				for (var i = 0; i < scriptsLoadedCallbacks.length; ++i)
				{
					var loadedCallback = scriptsLoadedCallbacks[i];
					loadedCallback && loadedCallback();
				}

				// Clear callbacks - should we do this???
				scriptsLoadedCallbacks = [];
			}
		};

		// onerror fires for things like malformed URLs and 404's.
		// If this function is called, the matching onload will not be called and
		// scriptsLoaded will not fire.
		script.onerror = function()
		{
			throw "Could not load script '" + url + "'";
		};

		// Attaching the script tag to the document starts loading the script.
		document.head.appendChild(script);
	};

	/**
	 * Load array of scripts.
	 * @param {array} array - Array of URL or path name stringa.
	 * Relative paths are relative to the HTML file that initiated
	 * script loading.
	 * @param {function} loadedCallback - Optional parameterless
	 * function called when all scripts in the array has loaded.
	 * @public
	 */
	evothings.loadScripts = function(array, loadedCallback)
	{
		var lib = array.shift();
		if (!lib)
		{
			// Array is empty and all scripts are loaded.
			loadedCallback && loadedCallback();
		}
		else
		{
			// Load next script.
			evothings.loadScript(lib, function() {
				evothings.loadScripts(array, loadedCallback);
			});
		}
	};

	/**
	 * Experimental.
	 * Mark a script as loaded. This is useful if a script is designed
	 * to be included both in HTML and in JavaScript.
	 * @param {string} pathOrURL - URL or path to the script. Relative paths are
	 * relative to the HTML file that initiated script loading.
	 * @public
	 */
	evothings.markScriptAsLoaded = function(pathOrURL)
	{
		loadedScripts[url] = 'loadingcomplete';
	};

	/**
	 *
Add a callback that will be called when all scripts are loaded.

	 *
It is good practise to always use this function when
	 * loading script asynchronously or using a library that does so.

	 * @param  {function} callback - Parameterless function that will
	 * be called when all scripts have finished loading.
	 * @public
	 */
	evothings.scriptsLoaded = function(callback)
	{
		// If scripts are already loaded call the callback directly,
		// else add the callback to the callbacks array.
		if (0 != Object.keys(loadedScripts).length &&
			0 == scriptLoadingCounter)
		{
			callback && callback();
		}
		else
		{
			scriptsLoadedCallbacks.push(callback);
		}
	};

	/* ------------------ Debugging ------------------ */

	/**
	 * Print a JavaScript object (dictionary). For debugging.
	 *
	 * @param {Object} obj - Object to print.
	 * @param {function} printFun - print function (optional - defaults to
	 * console.log if not given).
	 *
	 * @example
	 * var obj = { company: 'Evothings', field: 'IoT' };
	 * evothings.printObject(obj);
	 * evothings.printObject(obj, console.log);
	 *
	 * @public
	 */
	evothings.printObject = function(obj, printFun)
	{
		printFun = printFun || console.log;
		function print(obj, level)
		{
			var indent = new Array(level + 1).join('  ');
			for (var prop in obj)
			{
				if (obj.hasOwnProperty(prop))
				{
					var value = obj[prop];
					if (typeof value == 'object')
					{
						printFun(indent + prop + ':');
						print(value, level + 1);
					}
					else
					{
						printFun(indent + prop + ': ' + value);
					}
				}
			}
		}
		print(obj, 0);
	};

	/* ------------------ Platform check ------------------ */

	/**
	 * @namespace
	 * @description Namespace for platform check functions.
	 */
	evothings.os = {};

	/**
	 * Returns true if current platform is iOS, false if not.
	 * @return {boolean} true if platform is iOS, false if not.
	 * @public
	 */
	evothings.os.isIOS = function()
	{
		return /iP(hone|ad|od)/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is iOS 7, false if not.
	 * @return {boolean} true if platform is iOS 7, false if not.
	 * @public
	 */
	evothings.os.isIOS7 = function()
	{
		return /iP(hone|ad|od).*OS 7/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is Android, false if not.
	 * @return {boolean} true if platform is Android, false if not.
	 * @public
	 */
	evothings.os.isAndroid = function()
	{
		return /Android|android/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is Windows Phone, false if not.
	 * @return {boolean} true if platform is Windows Phone, false if not.
	 * @public
	 */
	evothings.os.isWP = function()
	{
		return /Windows Phone/.test(navigator.userAgent);
	};

	/* ------------------ Load UI script ------------------ */

	evothings.loadScript('libs/evothings/ui/ui.js');
})();
<commit_msg>Removed automatic loading of UI script, as this can conflict with UI frameworks like Ionic. Instead include this script manually as needed in example apps.<commit_after>;(function()
{
	/**
	 * @namespace
	 * @description
Functions for loading scripts asynchronously,
	 * detecting platform, and other common application functionality.

	 * @alias evothings
	 * @public
	 */
	var evothings = window.evothings || {};
	window.evothings = evothings;

	/* ------------------ Script loading ------------------ */

	var scriptLoadingCounter = 0;
	var loadedScripts = {};
	var scriptsLoadedCallbacks = [];

	/* Make sure to catch any DOMContentLoaded events occurring before
	 * asynchronous loading of scripts. Those scripts, like ui.js, should check
	 * this variable before listening for the event. */
	evothings.gotDOMContentLoaded = false

	window.addEventListener('DOMContentLoaded', function(e)
	{
		evothings.gotDOMContentLoaded = true
	})

	/**
	 * Load a script.
	 * @param {string} url - URL or path to the script. Relative paths are
	 * relative to the HTML file that initiated script loading.
	 * @param {function} callback - Optional parameterless function that will
	 * be called when the script has loaded.
	 * @public
	 */
	evothings.loadScript = function(url, callback)
	{
		// If script is already loaded call callback directly and return.
		if (loadedScripts[url])
		{
			callback && callback();
			return;
		}

		// Add script to dictionary of loaded scripts.
		loadedScripts[url] = 'loadingstarted';
		++scriptLoadingCounter;

		// Create script tag.
		var script = document.createElement('script');
		script.type = 'text/javascript';
		script.src = url;

		// Bind the onload event.
		script.onload = function()
		{
			// Mark as loaded.
			loadedScripts[url] = 'loadingcomplete';
			--scriptLoadingCounter;

			// Call callback if given.
			callback && callback();

			// Call scripts loaded callbacks if this was the last script loaded.
			if (0 == scriptLoadingCounter)
			{
				for (var i = 0; i < scriptsLoadedCallbacks.length; ++i)
				{
					var loadedCallback = scriptsLoadedCallbacks[i];
					loadedCallback && loadedCallback();
				}

				// Clear callbacks - should we do this???
				scriptsLoadedCallbacks = [];
			}
		};

		// onerror fires for things like malformed URLs and 404's.
		// If this function is called, the matching onload will not be called and
		// scriptsLoaded will not fire.
		script.onerror = function()
		{
			throw "Could not load script '" + url + "'";
		};

		// Attaching the script tag to the document starts loading the script.
		document.head.appendChild(script);
	};

	/**
	 * Load array of scripts.
	 * @param {array} array - Array of URL or path name stringa.
	 * Relative paths are relative to the HTML file that initiated
	 * script loading.
	 * @param {function} loadedCallback - Optional parameterless
	 * function called when all scripts in the array has loaded.
	 * @public
	 */
	evothings.loadScripts = function(array, loadedCallback)
	{
		var lib = array.shift();
		if (!lib)
		{
			// Array is empty and all scripts are loaded.
			loadedCallback && loadedCallback();
		}
		else
		{
			// Load next script.
			evothings.loadScript(lib, function() {
				evothings.loadScripts(array, loadedCallback);
			});
		}
	};

	/**
	 * Experimental.
	 * Mark a script as loaded. This is useful if a script is designed
	 * to be included both in HTML and in JavaScript.
	 * @param {string} pathOrURL - URL or path to the script. Relative paths are
	 * relative to the HTML file that initiated script loading.
	 * @public
	 */
	evothings.markScriptAsLoaded = function(pathOrURL)
	{
		loadedScripts[url] = 'loadingcomplete';
	};

	/**
	 *
Add a callback that will be called when all scripts are loaded.

	 *
It is good practise to always use this function when
	 * loading script asynchronously or using a library that does so.

	 * @param  {function} callback - Parameterless function that will
	 * be called when all scripts have finished loading.
	 * @public
	 */
	evothings.scriptsLoaded = function(callback)
	{
		// If scripts are already loaded call the callback directly,
		// else add the callback to the callbacks array.
		if (0 != Object.keys(loadedScripts).length &&
			0 == scriptLoadingCounter)
		{
			callback && callback();
		}
		else
		{
			scriptsLoadedCallbacks.push(callback);
		}
	};

	/* ------------------ Debugging ------------------ */

	/**
	 * Print a JavaScript object (dictionary). For debugging.
	 *
	 * @param {Object} obj - Object to print.
	 * @param {function} printFun - print function (optional - defaults to
	 * console.log if not given).
	 *
	 * @example
	 * var obj = { company: 'Evothings', field: 'IoT' };
	 * evothings.printObject(obj);
	 * evothings.printObject(obj, console.log);
	 *
	 * @public
	 */
	evothings.printObject = function(obj, printFun)
	{
		printFun = printFun || console.log;
		function print(obj, level)
		{
			var indent = new Array(level + 1).join('  ');
			for (var prop in obj)
			{
				if (obj.hasOwnProperty(prop))
				{
					var value = obj[prop];
					if (typeof value == 'object')
					{
						printFun(indent + prop + ':');
						print(value, level + 1);
					}
					else
					{
						printFun(indent + prop + ': ' + value);
					}
				}
			}
		}
		print(obj, 0);
	};

	/* ------------------ Platform check ------------------ */

	/**
	 * @namespace
	 * @description Namespace for platform check functions.
	 */
	evothings.os = {};

	/**
	 * Returns true if current platform is iOS, false if not.
	 * @return {boolean} true if platform is iOS, false if not.
	 * @public
	 */
	evothings.os.isIOS = function()
	{
		return /iP(hone|ad|od)/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is iOS 7, false if not.
	 * @return {boolean} true if platform is iOS 7, false if not.
	 * @public
	 */
	evothings.os.isIOS7 = function()
	{
		return /iP(hone|ad|od).*OS 7/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is Android, false if not.
	 * @return {boolean} true if platform is Android, false if not.
	 * @public
	 */
	evothings.os.isAndroid = function()
	{
		return /Android|android/.test(navigator.userAgent);
	};

	/**
	 * Returns true if current platform is Windows Phone, false if not.
	 * @return {boolean} true if platform is Windows Phone, false if not.
	 * @public
	 */
	evothings.os.isWP = function()
	{
		return /Windows Phone/.test(navigator.userAgent);
	};
})();
<|endoftext|>


<commit_before>//
// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// 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.
//

/**
* 1. Demonstrates how to upload all files from a given directory in parallel
*
* 2. Demonstrates how to download all files from a given blob container to a given destination directory.
*
* 3. Demonstrate making requests using AccessConditions.
*/

var fs = require('fs');

var azure;
if (fs.existsSync('absolute path to azure-storage.js')) {
  azure = require('absolute path to azure-storage');
} else {
  azure = require('azure-storage');
}

var container = 'updownsample3';
var blob = 'updownsample';
var blobAccess = 'updownaccesssample';

var blobService = azure.createBlobService()
      .withFilter(new azure.ExponentialRetryPolicyFilter());

// optionally set a proxy
/*var proxy = {
  protocol: 'http:',
  host: '127.0.0.1',
  port: 8888
};

blobService.setProxy(proxy);
*/

function uploadSample() {
  var processArguments = process.argv;
  if (processArguments.length !== 4) {
    console.log('Incorrect number of arguments. Should be: srcPath destPath');
    process.exit(1);
  }

  var srcPath = processArguments[2];
  var destPath = processArguments[3];

  console.log('Starting blobuploaddownloadsample.');

  // Create the container
  createContainer(container)
  .then(function(container) {
    console.log('Created the container ' + container);
    // Demonstrates how to upload all files from a given directoy
    return uploadBlobs(srcPath, container);
  })
  .then(function(container) {
    // Demonstrates how to download all files from a given
    // blob container to a given destination directory.
    return downloadBlobs(container, destPath);
  })
  .then(function(container) {
      // Demonstrate making requests using AccessConditions.
      return useAccessCondition(container);
  })
  .then(function(container) {
      console.log('Ending blobuploaddownloadsample.');
      return deleteContainer(container);
  })
  .then(function() {
    console.log('Deleted the container ' + container);
  })
  .catch(function(error) {
    console.error(error);
  });
}

function createContainer (container) {
  return new Promise(function(resolve, reject) {
    // Create the container.
    blobService.createContainerIfNotExists(container, function (error) {
      if (error) {
        reject(error);
      } else {
        resolve(container);
      }
    });
  });
}

function uploadBlobs(sourceDirectoryPath, containerName) {
  console.log('Entering uploadBlobs.');
  return new Promise(function(resolve, reject) {
    // validate directory is valid.
    if (!fs.existsSync(sourceDirectoryPath)) {
      reject(sourceDirectoryPath + ' is an invalid directory path.');
    } else {
      // Search the directory and generate a list of files to upload.
      walk(sourceDirectoryPath, function (error, files) {
        if (error) {
          reject(error);
        } else {
          var finished = 0;
          // generate and schedule an upload for each file
          files.forEach(function (file) {
            var blobName = file.replace(/^.*[\\\/]/, '');
            blobService.createBlockBlobFromLocalFile(containerName, blobName, file, function (error) {
              finished++;
              if (error) {
                reject(error);
              } else {
                console.log(' Blob ' + blobName + ' upload finished.');
                if (finished === files.length) {
                  // Wait until all workers complete and the blobs are uploaded to the server.
                  console.log('All files uploaded');
                  resolve(containerName);
                }
              }
            });
          });
        }
      });
    }
  });
}

function downloadBlobs(containerName, destinationDirectoryPath) {
  console.log('Entering downloadBlobs.');
  return new Promise(function(resolve, reject) {
    // Validate directory
    if (!fs.existsSync(destinationDirectoryPath)) {
      console.log(destinationDirectoryPath + ' does not exist. Attempting to create this directory...');
      fs.mkdirSync(destinationDirectoryPath);
      console.log(destinationDirectoryPath + ' created.');
    }
    // NOTE: does not handle pagination.
    blobService.listBlobsSegmented(containerName, null, function (error, result) {
      if (error) {
        reject(error);
      } else {
        var blobs = result.entries;
        var blobsDownloaded = 0;
        blobs.forEach(function (blob) {
            blobService.getBlobToLocalFile(containerName, blob.name, destinationDirectoryPath + '/' + blob.name, function (error2) {
            blobsDownloaded++;

            if (error2) {
              reject(error2);
            } else {
              console.log(' Blob ' + blob.name + ' download finished.');
              if (blobsDownloaded === blobs.length) {
                // Wait until all workers complete and the blobs are downloaded
                console.log('All files downloaded');
                resolve(containerName);
              }
            }
          });
        });
      }
    });
  });
}

function useAccessCondition(containerName) {
  console.log('Entering useAccessCondition.');
  return new Promise(function(resolve, reject) {
    // Create a blob.
    blobService.createBlockBlobFromText(containerName, blobAccess, 'hello', function (error, blobInformation) {
      if (error) {
        reject(error);
      } else {
        console.log(' Created the blob ' + blobInformation.name);
        console.log(' Blob Etag is: ' + blobInformation.etag);
        // Use the If-not-match ETag condition to access the blob. By
        // using the IfNoneMatch condition we are asserting that the blob needs
        // to have been modified in order to complete the request. In this
        // sample no other client is accessing the blob, so this will fail as
        // expected.
        var options = { accessConditions: { EtagNonMatch: blobInformation.etag} };
        blobService.createBlockBlobFromText(containerName, blobInformation.name, 'new hello', options, function (error2) {
          if (error2 && error2.statusCode === 412 && error2.code === 'ConditionNotMet') {
            console.log('Attempted to recreate the blob with the if-none-match access condition and got the expected exception.');
            resolve(containerName);
          } else {
            reject('Blob was incorrectly updated');
          }
        });
      }
    });
  });
}

function deleteContainer (container) {
  return new Promise(function(resolve, reject) {
    // Delete the container.
    blobService.deleteContainerIfExists(container, function (error) {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  });
}

// Utility function

var walk = function (dir, done) {
  var results = [];
  fs.readdir(dir, function (err, list) {
    if (err) return done(err);
    var i = 0;
    (function next() {
      var file = list[i++];
      if (!file) return done(null, results);
      file = dir + '/' + file;
      fs.stat(file, function (err2, stat) {
        if (stat && stat.isDirectory()) {
          walk(file, function (err3, res) {
            results = results.concat(res);
            next();
          });
        } else {
          results.push(file);
          next();
        }
      });
    })();
  });
};

uploadSample();
<commit_msg>Promise based files listing<commit_after>//
// Copyright (c) Microsoft and contributors.  All rights reserved.
//
// 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.
//

/**
* 1. Demonstrates how to upload all files from a given directory in parallel
*
* 2. Demonstrates how to download all files from a given blob container to a given destination directory.
*
* 3. Demonstrate making requests using AccessConditions.
*/

var fs = require('fs');

var azure;
if (fs.existsSync('absolute path to azure-storage.js')) {
  azure = require('absolute path to azure-storage');
} else {
  azure = require('azure-storage');
}

var container = 'updownsample3';
var blob = 'updownsample';
var blobAccess = 'updownaccesssample';

var blobService = azure.createBlobService()
      .withFilter(new azure.ExponentialRetryPolicyFilter());

// optionally set a proxy
/*var proxy = {
  protocol: 'http:',
  host: '127.0.0.1',
  port: 8888
};

blobService.setProxy(proxy);
*/

function uploadSample() {
  var processArguments = process.argv;
  if (processArguments.length !== 4) {
    console.log('Incorrect number of arguments. Should be: srcPath destPath');
    process.exit(1);
  }

  var srcPath = processArguments[2];
  var destPath = processArguments[3];

  console.log('Starting blobuploaddownloadsample.');

  // Create the container
  createContainer(container)
  .then(function(container) {
    console.log('Created the container ' + container);
    // Demonstrates how to upload all files from a given directoy
    return uploadBlobs(srcPath, container);
  })
  .then(function(container) {
    // Demonstrates how to download all files from a given
    // blob container to a given destination directory.
    return downloadBlobs(container, destPath);
  })
  .then(function(container) {
      // Demonstrate making requests using AccessConditions.
      return useAccessCondition(container);
  })
  .then(function(container) {
      console.log('Ending blobuploaddownloadsample.');
      return deleteContainer(container);
  })
  .then(function() {
    console.log('Deleted the container ' + container);
  })
  .catch(function(error) {
    console.error(error);
  });
}

function createContainer (container) {
  return new Promise(function(resolve, reject) {
    // Create the container.
    blobService.createContainerIfNotExists(container, function (error) {
      if (error) {
        reject(error);
      } else {
        resolve(container);
      }
    });
  });
}

function uploadBlobs(sourceDirectoryPath, containerName) {
  console.log('Entering uploadBlobs.');
  return new Promise(function(resolve, reject) {
    // validate directory is valid.
    if (!fs.existsSync(sourceDirectoryPath)) {
      reject(sourceDirectoryPath + ' is an invalid directory path.');
    } else {
      // Search the directory and generate a list of files to upload.
      listAllFiles(sourceDirectoryPath)
      .then(function(files) {
        var finished = 0;
        // generate and schedule an upload for each file
        files.forEach(function (file) {
          var blobName = file.replace(/^.*[\\\/]/, '');
          blobService.createBlockBlobFromLocalFile(containerName, blobName, file, function (error) {
            finished++;
            if (error) {
              reject(error);
            } else {
              console.log(' Blob ' + blobName + ' upload finished.');
              if (finished === files.length) {
                // Wait until all workers complete and the blobs are uploaded to the server.
                console.log('All files uploaded');
                resolve(containerName);
              }
            }
          });
        });
      })
      .catch(function(error) {
        reject(error);
      });
    }
  });
}

function downloadBlobs(containerName, destinationDirectoryPath) {
  console.log('Entering downloadBlobs.');
  return new Promise(function(resolve, reject) {
    // Validate directory
    if (!fs.existsSync(destinationDirectoryPath)) {
      console.log(destinationDirectoryPath + ' does not exist. Attempting to create this directory...');
      fs.mkdirSync(destinationDirectoryPath);
      console.log(destinationDirectoryPath + ' created.');
    }
    // NOTE: does not handle pagination.
    blobService.listBlobsSegmented(containerName, null, function (error, result) {
      if (error) {
        reject(error);
      } else {
        var blobs = result.entries;
        var blobsDownloaded = 0;
        blobs.forEach(function (blob) {
            blobService.getBlobToLocalFile(containerName, blob.name, destinationDirectoryPath + '/' + blob.name, function (error2) {
            blobsDownloaded++;

            if (error2) {
              reject(error2);
            } else {
              console.log(' Blob ' + blob.name + ' download finished.');
              if (blobsDownloaded === blobs.length) {
                // Wait until all workers complete and the blobs are downloaded
                console.log('All files downloaded');
                resolve(containerName);
              }
            }
          });
        });
      }
    });
  });
}

function useAccessCondition(containerName) {
  console.log('Entering useAccessCondition.');
  return new Promise(function(resolve, reject) {
    // Create a blob.
    blobService.createBlockBlobFromText(containerName, blobAccess, 'hello', function (error, blobInformation) {
      if (error) {
        reject(error);
      } else {
        console.log(' Created the blob ' + blobInformation.name);
        console.log(' Blob Etag is: ' + blobInformation.etag);
        // Use the If-not-match ETag condition to access the blob. By
        // using the IfNoneMatch condition we are asserting that the blob needs
        // to have been modified in order to complete the request. In this
        // sample no other client is accessing the blob, so this will fail as
        // expected.
        var options = { accessConditions: { EtagNonMatch: blobInformation.etag} };
        blobService.createBlockBlobFromText(containerName, blobInformation.name, 'new hello', options, function (error2) {
          if (error2 && error2.statusCode === 412 && error2.code === 'ConditionNotMet') {
            console.log('Attempted to recreate the blob with the if-none-match access condition and got the expected exception.');
            resolve(containerName);
          } else {
            reject('Blob was incorrectly updated');
          }
        });
      }
    });
  });
}

function deleteContainer (container) {
  return new Promise(function(resolve, reject) {
    // Delete the container.
    blobService.deleteContainerIfExists(container, function (error) {
      if (error) {
        reject(error);
      } else {
        resolve();
      }
    });
  });
}

// Utility function

function listDirectory(dir) {
  return new Promise(function(resolve, reject) {
    fs.readdir(dir, function(error, files) {
      if(error) {
        reject(error);
      } else {
        resolve(files);
      }
    })
  });
}

function getFileStat(file) {
  return new Promise(function(resolve, reject) {
    fs.stat(file, function(error, stat) {
      if(error) {
        reject(error);
      } else {
        resolve(stat);
      }
    });
  });
}

function listAllFiles(dir) {
  return listDirectory(dir)
  .then(function(files) {
    return Promise.all(files.map(function(file) {
      file = dir + '/' + file;
      return getFileStat(file)
      .then(function(stat) {
        if(stat.isDirectory()) {
          return listAllFiles(file);
        } else {
          return file;
        }
      });
    }));
  })
  .then(function(files) {
    return Array.prototype.concat.apply([], files);
  });
}

uploadSample();
<|endoftext|>


<commit_before>const buildHelp = (client, msg) => new Promise((resolve) => {
  const help = {};
  const mps = [];

  const commandNames = Array.from(client.commands.keys());
  const longest = commandNames.reduce((long, str) => Math.max(long, str.length), 0);
  const permissionLevel = msg.guild ? msg.member.permLevel : msg.author.permLevel;

  client.commands.filter(cmd => permissionLevel >= cmd.conf.permLevel).forEach((command) => {
    mps.push(new Promise((res) => {
      const cat = command.help.category;
      const subcat = command.help.subCategory;
      if (!help.hasOwnProperty(cat)) help[cat] = {};
      if (!help[cat].hasOwnProperty(subcat)) help[cat][subcat] = [];
      help[cat][subcat].push(`${msg.guildConf.prefix}${command.help.name}${" ".repeat(longest - command.help.name.length)} :: ${command.help.description}`);
      res();
    }));
  });
  Promise.all(mps).then(resolve(help));
});

exports.run = (client, msg, [cmd]) => {
  if (!cmd) {
    buildHelp(client, msg)
      .then((help) => {
        const helpMessage = [];
        for (const key in help) {
          helpMessage.push(`**${key} Commands**: \`\`\`asciidoc`);
          for (const key2 in help[key]) {
            helpMessage.push(`= ${key2} =`);
            helpMessage.push(`${help[key][key2].join("\n")}\n`);
          }
          helpMessage.push("```\n\u200b");
        }
        if (!client.config.selfbot) {
          msg.author.send(helpMessage, { split: { char: "\u200b" } }).catch(e => client.funcs.log(e, "error"));
          if (msg.channel.type.toLowerCase() !== "dm") {
            msg.reply("Commands have been sent to your DMs.");
          }
        } else {
          msg.channel.send(helpMessage, { split: { char: "\u200b" } })
            .catch(e => client.funcs.log(e, "error"));
        }
      });
  } else if (client.commands.has(cmd)) {
    cmd = client.commands.get(cmd);
    if (!client.config.selfbot) {
      msg.author.send(`= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}\nExtended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`, { code: "asciidoc" });
    } else {
      msg.channel.send(`= ${cmd.help.name} = \n${cmd.help.description}\nusage :: ${client.funcs.fullUsage(client, cmd)}\nExtended Help ::\n${cmd.help.extendedHelp ? cmd.help.extendedHelp : "No extended help available."}`, { code: "asciidoc" });
    }
  }
};

exports.conf = {
  enabled: true,
  runIn: ["text", "dm", "group"],
  aliases: [],
  permLevel: 0,
  botPerms: [],
  requiredFuncs: [],
};

exports.help = {
  name: "help",
  description: "Display help for a command.",
  usage: "[command:str]",
  usageDelim: "",
};
<commit_msg>Delete help.js<commit_after><|endoftext|>


<commit_before>var fs = require('fs');
var pathlib = require('path');
var Stream = require('stream').Stream;

// copy properties from right-most args to left-most
var extend = function (preserveExistingProperties) {
    var result = undefined;
    for (var i = 1; i < arguments.length; i++) {
        obj = arguments[i];

        // set initial result object to the first argument given
        if (!result) {
            result = obj;
            continue;
        }

        for (prop in obj) {
            if (Object.hasOwnProperty(obj, prop)) {
                // preserve preexisting child properties if specified
                if (preserveExistingProperties &&
                        Object.hasOwnProperty(result, prop)) {
                    continue;
                }
                result[prop] = obj[prop];
            }
        }
    }

    return result;
}

var TailingReadableStream = function () {
    // run Stream's init code on us, since we're a Stream
    Stream.call(this);

    // whether the stream can be read from
    this.readable = true;

    // time before a tailing file is considered 'done'
    this.timeout = 5000;

    // options passed to created ReadableStreams
    this._read_stream_options = {};

    this._path = null;
    this._stream = null;
    this._watcher = null;
    this._offset = 0;
    this._timeoutId = null;

    this._paused = false;
};

// 'inherit' from Stream
TailingReadableStream.prototype = Object.create(Stream.prototype, {
    constructor: {
        value: TailingReadableStream,
        enumerable: false
    }
});
extend(true, TailingReadableStream, Stream);

// create a new TailingReadableStream and return it
TailingReadableStream.open = function (path, options) {
    options = options || {};

    var file = new TailingReadableStream();

    // override the timeout if present in options
    if (Object.hasOwnProperty(options, 'timeout')) {
        file.timeout = options.timeout;
    }

    // set the reading start point if specified
    if (Object.hasOwnProperty(options, 'start')) {
        file._offset = options.start;
    }

    // store options for use when opening ReadableStreams
    file._read_stream_options = {};
    extend(false, file._read_stream_options, options);

    file._path = path;
    file._watch();

    return file;
};

// start watching the file for size changes
TailingReadableStream.prototype._watch = function () {
    var self = this;

    // watch the file for changes
    this._watcher = fs.watch(this._path);
    this._watcher.on('change', function (event, fileName) {
        // reset the kill switch for inactivity on every non-paused change
        self._resetTimeoutKillswitch();

        // start a new stream if one isn't running and a change happened
        if (!self._stream && self.readable && event === 'change') {
            // send all data from the last byte sent to EOF
            var readOpts = extend(false, {}, self._read_stream_options);
            readOpts.start = self._offset;
            self._stream = fs.createReadStream(self._path, readOpts);

            // pipe data through our own event while tracking its progress
            self._stream.on('data', function (data) {
                // track the amount of data that's been read, then forward
                self._offset += data.length;
                self.emit('data', data)
            });

            // forward errors and close the stream when received
            self._stream.on('error', function (exception) {
                self.destroy();
                self.emit('error', exception);
                self.emit('close');
            });

            // when we reach the end, destroy the stream and null it out so a
            // new one will be created for the next file change.
            self._stream.on('end', function () {
                self._stream.destroy();
                self._stream = null;
            });
        }
    });

    // destroy then forward errors
    this._watcher.on('error', function (exception) {
        self.destroy();
        self.emit('error', exception);
        self.emit('close');
    });

    // trigger initial change event to force streaming of existing file data
    this._watcher.emit('change', 'change', this._path);
};

// update the encoding of data sent to 'data' events
TailingReadableStream.prototype.setEncoding = function (encoding) {
    // set the default
    encoding = encoding || 'utf8';

    // update the read options
    this._read_stream_options.encoding = encoding;

    // update any live stream
    if (this._stream) {
        this._stream.setEncoding(encoding);
    }
};

// pause stream reading for a while
TailingReadableStream.prototype.pause = function () {
    if (!this._paused) {
        this._paused = true;

        // stop watching the file (restarts when resume is called)
        this._watcher.close();
        this._watcher = null;

        // clear the timeout kill switch
        clearTimeout(this._timeoutId);
        this._timeoutId = null;

        // pause any existing stream
        if (this._stream) {
            this._stream.pause();
        }
    }
};

// resume watching/reading from the file
TailingReadableStream.prototype.resume = function () {
    if (this._paused) {
        this._paused = false;

        // resume any existing stream and start watching/reading again
        this._watch();
        if (this._stream) {
            this._stream.resume();
        }
    }
};

// start checking for changes, clearing any existing checks
TailingReadableStream.prototype._resetTimeoutKillswitch = function () {
    // set a timeout to check for non-activity. setTimeout() is used to allow
    // the user to dynamically change the timeout duration, if desired.
    clearTimeout(this._timeoutId);

    // set a new timeout unless timeout is disabled
    if (this.timeout) {
        this._timeoutId = setTimeout(this._timeoutKillswitch.bind(this),
                this.timeout);
    }
};

// emit an 'end' event, then destroy the stream
TailingReadableStream.prototype._timeoutKillswitch = function () {
    this.destroy();
    this.emit('end');
    this.emit('close');
};

// stop watching/reading the file and stop emitting events
TailingReadableStream.prototype.destroy = function () {
    // pause to stop the watcher and clear the kill switch
    this.pause();

    // close any existing read stream
    if (this._stream) {
        this._stream.destroy();
        this._stream = null;
    }

    // mark that we're no longer readable or paused
    this.readable = false;
    this._paused = false;
};

// build a tailing readable stream given a path and some options
var createTailingReadStream = function (path, options) {
    return TailingReadableStream.open(path, options);
};

// exports
module.exports.createReadStream = createTailingReadStream;
module.exports.TailingReadableStream = TailingReadableStream;
<commit_msg>Remove redundant variable set<commit_after>var fs = require('fs');
var pathlib = require('path');
var Stream = require('stream').Stream;

// copy properties from right-most args to left-most
var extend = function (preserveExistingProperties) {
    var result = undefined;
    for (var i = 1; i < arguments.length; i++) {
        obj = arguments[i];

        // set initial result object to the first argument given
        if (!result) {
            result = obj;
            continue;
        }

        for (prop in obj) {
            if (Object.hasOwnProperty(obj, prop)) {
                // preserve preexisting child properties if specified
                if (preserveExistingProperties &&
                        Object.hasOwnProperty(result, prop)) {
                    continue;
                }
                result[prop] = obj[prop];
            }
        }
    }

    return result;
}

var TailingReadableStream = function () {
    // run Stream's init code on us, since we're a Stream
    Stream.call(this);

    // whether the stream can be read from
    this.readable = true;

    // time before a tailing file is considered 'done'
    this.timeout = 5000;

    // options passed to created ReadableStreams
    this._read_stream_options = {};

    this._path = null;
    this._stream = null;
    this._watcher = null;
    this._offset = 0;
    this._timeoutId = null;

    this._paused = false;
};

// 'inherit' from Stream
TailingReadableStream.prototype = Object.create(Stream.prototype, {
    constructor: {
        value: TailingReadableStream,
        enumerable: false
    }
});
extend(true, TailingReadableStream, Stream);

// create a new TailingReadableStream and return it
TailingReadableStream.open = function (path, options) {
    options = options || {};

    var file = new TailingReadableStream();

    // override the timeout if present in options
    if (Object.hasOwnProperty(options, 'timeout')) {
        file.timeout = options.timeout;
    }

    // set the reading start point if specified
    if (Object.hasOwnProperty(options, 'start')) {
        file._offset = options.start;
    }

    // store options for use when opening ReadableStreams
    extend(false, file._read_stream_options, options);

    file._path = path;
    file._watch();

    return file;
};

// start watching the file for size changes
TailingReadableStream.prototype._watch = function () {
    var self = this;

    // watch the file for changes
    this._watcher = fs.watch(this._path);
    this._watcher.on('change', function (event, fileName) {
        // reset the kill switch for inactivity on every non-paused change
        self._resetTimeoutKillswitch();

        // start a new stream if one isn't running and a change happened
        if (!self._stream && self.readable && event === 'change') {
            // send all data from the last byte sent to EOF
            var readOpts = extend(false, {}, self._read_stream_options);
            readOpts.start = self._offset;
            self._stream = fs.createReadStream(self._path, readOpts);

            // pipe data through our own event while tracking its progress
            self._stream.on('data', function (data) {
                // track the amount of data that's been read, then forward
                self._offset += data.length;
                self.emit('data', data)
            });

            // forward errors and close the stream when received
            self._stream.on('error', function (exception) {
                self.destroy();
                self.emit('error', exception);
                self.emit('close');
            });

            // when we reach the end, destroy the stream and null it out so a
            // new one will be created for the next file change.
            self._stream.on('end', function () {
                self._stream.destroy();
                self._stream = null;
            });
        }
    });

    // destroy then forward errors
    this._watcher.on('error', function (exception) {
        self.destroy();
        self.emit('error', exception);
        self.emit('close');
    });

    // trigger initial change event to force streaming of existing file data
    this._watcher.emit('change', 'change', this._path);
};

// update the encoding of data sent to 'data' events
TailingReadableStream.prototype.setEncoding = function (encoding) {
    // set the default
    encoding = encoding || 'utf8';

    // update the read options
    this._read_stream_options.encoding = encoding;

    // update any live stream
    if (this._stream) {
        this._stream.setEncoding(encoding);
    }
};

// pause stream reading for a while
TailingReadableStream.prototype.pause = function () {
    if (!this._paused) {
        this._paused = true;

        // stop watching the file (restarts when resume is called)
        this._watcher.close();
        this._watcher = null;

        // clear the timeout kill switch
        clearTimeout(this._timeoutId);
        this._timeoutId = null;

        // pause any existing stream
        if (this._stream) {
            this._stream.pause();
        }
    }
};

// resume watching/reading from the file
TailingReadableStream.prototype.resume = function () {
    if (this._paused) {
        this._paused = false;

        // resume any existing stream and start watching/reading again
        this._watch();
        if (this._stream) {
            this._stream.resume();
        }
    }
};

// start checking for changes, clearing any existing checks
TailingReadableStream.prototype._resetTimeoutKillswitch = function () {
    // set a timeout to check for non-activity. setTimeout() is used to allow
    // the user to dynamically change the timeout duration, if desired.
    clearTimeout(this._timeoutId);

    // set a new timeout unless timeout is disabled
    if (this.timeout) {
        this._timeoutId = setTimeout(this._timeoutKillswitch.bind(this),
                this.timeout);
    }
};

// emit an 'end' event, then destroy the stream
TailingReadableStream.prototype._timeoutKillswitch = function () {
    this.destroy();
    this.emit('end');
    this.emit('close');
};

// stop watching/reading the file and stop emitting events
TailingReadableStream.prototype.destroy = function () {
    // pause to stop the watcher and clear the kill switch
    this.pause();

    // close any existing read stream
    if (this._stream) {
        this._stream.destroy();
        this._stream = null;
    }

    // mark that we're no longer readable or paused
    this.readable = false;
    this._paused = false;
};

// build a tailing readable stream given a path and some options
var createTailingReadStream = function (path, options) {
    return TailingReadableStream.open(path, options);
};

// exports
module.exports.createReadStream = createTailingReadStream;
module.exports.TailingReadableStream = TailingReadableStream;
<|endoftext|>