Source: list.js

/* global Lang,app */

'use strict';

define('list', ['jquery', 'ko'], function($, ko) {
    /**
     * This class describe the client behavior of the item lists.
     * Lists are available in window by `app.lists[id]`
     *
     * @param {Object} data The list initial parameters. this object must contain :
     *                      - id : The list id
     *                      - action : The URL to load to refresh the list
     *                      - target (optionnal) : Where the list must be refreshed (default, it will be replace itself)
     *                      - userParam : The filters, orders and navigation parameters previously set by the user
     * @class List
     */
    function List(data) {
        this.id = data.id;
        this.action = data.action;
        this.target = data.target;
        this.maxPages = ko.observable();
        this.recordNumber = ko.observable(0);


        this.node = $('#' + this.id);
        this.wrapper = this.node.parent();
        this.navigationSection = this.wrapper.find('.list-navigation');
        this.titleLine = this.node.find('.list-title-line');
        this.refreshContainer = this.node.find('.list > tbody');

        // Get the list display parameters (number of lines, page number, searches and sorts)
        var params = data.userParam || {};

        this.searches = params.searches || {};
        this.sorts = params.sorts || {};
        this.page = ko.observable(params.page || List.DEFAULT_PAGE_NUMBER);
        this.lines = ko.observable(params.lines || List.DEFAULT_LINES_NUMBER);

        this.fields = {};
        for (var j = 0; j < data.fields.length; j++) {
            var field = data.fields[j];

            this.fields[field] = {
                name : field,
                search : ko.observable(this.searches[field]),
                sort : ko.observable(this.sorts[field])
            };
        }

        // The label displaying the number of the list results
        this.recordNumberLabel = ko.computed(function() {
            return Lang.get('main.list-results-number', {number : this.recordNumber()}, this.recordNumber());
        }.bind(this));

        this.initControls();
    }

    /**
     * The default delay before refreshing on search
     *
     * @var {int}
     */
    List.DEFAULT_SEARCH_DELAY = 400;


    /**
     * The default lines number to display
     *
     * @var {int}
     */
    List.DEFAULT_LINES_NUMBER = 20;

    /**
     * The default page number to display
     *
     * @var {int}
     */
    List.DEFAULT_PAGE_NUMBER = 1;




    /**
     * Refresh the list
     *
     * @param {Object} options - Additionnal options to set to the request
     * @returns {boolean} False
     * @memberOf List
     */
    List.prototype.refresh = function(options) {
        // Set the user filters
        var data = {
            lines : this.lines(),
            page : this.page(),
            searches : this.searches,
            sorts : this.sorts
        };

        var headers = options && options.headers || {};

        headers['X-List-Filter-' + this.id] = JSON.stringify(data);

        // Send the list is refreshing to the server
        var get = {
            refresh : 1
        };

        if (this.selected) {
            get.selected = this.selected;
        }

        // Load the new data from the server
        $.ajax({
            url: this.action,
            method : 'GET',
            headers : headers,
            data : get,
            cache : false
        })
        .done(function(response) {
            this.refreshContainer.html(response);
        }.bind(this))

        .fail(function() {
            app.notify('error', Lang.get('main.refresh-list-error'));
        });

        return false;
    };


    /**
     * Listen for list parameters changements to refresh the list
     *
     * @memberOf List
     */
    List.prototype.initControls = function() {
        if (this.navigationSection.length) {
            ko.applyBindings(this, this.navigationSection[0]);
        }
        if (this.titleLine.length) {
            ko.applyBindings(this, this.titleLine[0]);
        }

        var self = this;

        /**
         * Select all the lines
         */
        this.node.find('.list-checkbox-all').change(function() {
            self.node.find('.list-checkbox').prop('checked', $(this).is(':checked')).trigger('change');
        });

        /**
         * Select a line
         */
        this.node.find('.list-checkbox').change(function() {
            self.node.find('.list-checkbox-all').prop({
                checked : self.node.find('.list-checkbox:not(:checked)').length === 0
            });
        });


        /**
         * Change the number of lines per page
         */
        this.lines.subscribe(function() {
            this.refresh();
        }.bind(this));


        /**
         * Go to the page xx
         *
         * @param {int} value The page number to go on
         */
        this.page.subscribe(function(value) {
            if (isNaN(value)) {
                this.page(1);
                return;
            }

            if (value < 1) {
                this.page(1);
                return;
            }

            if (value > this.maxPages()) {
                this.page(this.maxPages());
                return;
            }

            this.refresh();
        }.bind(this));


        /**
         * Detect, when the max page number changed, to keep the page number lower than it
         *
         * @param {int} value The max page number
         */
        this.maxPages.subscribe(function(value) {
            if (this.page() > value) {
                this.page(value);
            }
        }.bind(this));


        $.each(this.fields, function(name, field) {
            /**
             * Sort the list
             *
             * @param {string} value The sort value : 'ASC' or 'DESC'
             */
            field.sort.subscribe(function(value) {
                if (!value) {
                    delete this.sorts[name];
                }
                else {
                    this.sorts[name] = value;
                }

                this.refresh();
            }.bind(this));


            /**
             * Type a search
             *
             * @param {string} value The search value
             */
            field.search.subscribe(function(value) {
                if (value) {
                    this.searches[name] = value;
                }
                else {
                    delete this.searches[name];
                }

                // Wait for 400 ms to refresh the list, in case the user enter new characters in this interval
                clearTimeout(this.searchTimeout);
                this.searchTimeout = setTimeout(
                    function() {
                        this.refresh();
                    }.bind(this),
                    List.DEFAULT_SEARCH_DELAY
                );
            }.bind(this));
        }.bind(this));
    };

    return List;
});