<!-- component template -->
<script type="text/x-template" id="grid-template">
  <div>
	  <template v-if="clientMode && searchableCols.length > 0">
		<strong>Search</strong> &nbsp; <input name="query" v-model="filterKey">
	  </template>
	  <div v-if="paginationAllowed">
		  <span class='col-xs-4 pull-left'>
			{{ this.pagination.showing}}
			&nbsp;	
			{{ fromRow }}
			&nbsp;
			--
			{{ toRow }}
			&nbsp;
			{{ this.pagination.of }}
			&nbsp;
			{{ totalRows }}
		  </span>
		  <span class='col-xs-4'>
			<button v-for='b in pagerButtons.backward' :disabled='b.disabled' :text='b.text' @click='changePage(b)'>{{ b.text }}</button>
			&nbsp;
			<input type='textbox' v-model='currentPage'></input>&nbsp;/&nbsp; {{ totalPages }}
			&nbsp;
			<button v-for='b in pagerButtons.forward' :disabled='b.disabled' :text='b.text' @click='changePage(b)'>{{ b.text }}</button>
		  </span>
		  <span class='col-xs-4 pull-right'>
			{{ pagination.perPage }}
			<input type='textbox' v-model='currentSize' />
		  </span>
	  </div>
	  <table>
		<thead>
		  <tr>
			<th v-for='c in viewableCols'
			  @click='sortBy(c)'
			  :class='sortKey.caseInsensitiveMatch(c.key) ? c.cssClass + " active" : c.cssClass'>
			  <input v-if='c.type.caseInsensitiveMatch("checkbox")' type='checkbox' @change='toggleSelectCol(c)' v-model='c.allSelected'></input>
			  {{ c.title | capitalize }}
			  <span v-if="c.sortable" class="arrow" :class="sortOrders[c.key] > 0 ? 'asc' : 'dsc'">
			  </span>
			</th>
			<th v-if='schema.operations && schema.operations.actions && schema.operations.actions.length > 0'>
				{{ schema.operations.colTitle | capitalize }}
			</th>
		  </tr>
		</thead>
		<tbody>
		  <template v-for="row in filteredRows" >
			<tr class='row'>
				<td v-for="c in viewableCols" :class='c.cssClass'>
					<a v-if="isClickable(c)" :href='getLink(c,row)' @click='callbackHandler(getCallbackInfo(c,row));' :disabled='row.data[c.key].disabled'>{{row.data[c.key].value}}</a>
					<input v-else-if="c.type.caseInsensitiveMatch('checkbox')" type='checkbox' v-model='row.data[c.key].value' @change='toggleSelect(c,row)' :disabled='row.data[c.key].disabled'></input>
					<input  v-else-if="c.type.caseInsensitiveMatch('textbox')" type='textbox' v-model='row.data[c.key].value' :disabled='row.data[c.key].disabled'></input>
					<i v-else-if="c.type.caseInsensitiveMatch('icon')" :class='row.data[c.key].cssClass'></i>
					<span v-else>
					  {{row.data[c.key].value}}
					</span>
				</td>
				<td v-if='schema.operations && schema.operations.actions && schema.operations.actions.length > 0'>
					<ul class="option-menu" v-if='row.operations && row.operations.length > 0'>
						<li class="dropdown">
							<a href="javascript:void(0);" class="dropdown-toggle" data-toggle="dropdown" role="button" aria-haspopup="true" aria-expanded="false">
								<i class="material-icons">menu</i>
							</a>
							<ul  class="dropdown-menu pull-right">
								<li v-for='a in schema.operations.actions' v-if='row.operations && row.operations.includes(a.action)'>
									<a :class='a.cssClass' :href='getLink(a,row)' @click='callbackHandler(getCallbackInfo(a,row))'>{{ a.title }}</a>
								</li>
							</ul>
						</li>
					</ul>
				</td>
			</tr>
			<tr v-if='row.extended && row.extended.visible' class='row-extended'>
				<td :colspan='viewableCols.length + 1'>
					<div v-for='d in row.extended.data' :class='d.cssClass'>
						<span class='row-extended-heading'>
							<h4>{{ d.heading }}</h4>
						</span>
						<span class='row-extended-text'>
							<template v-if="d.link || d.callback">
								<a :href='getLink(d,row)' @click='callbackHandler(getCallbackInfo(d,row))'>{{ d.text }}</a>
							</template>
							<template v-else>
								{{ d.text }}
							</template>
						</span>
					</div>
				</td>
			</tr>
		  </template>
		</tbody>
	  </table>
	  <div v-if="paginationAllowed">
		  <span class='col-xs-4 pull-left'>
			{{ this.pagination.showing}}
			&nbsp;	
			{{ fromRow }}
			&nbsp;
			--
			{{ toRow }}
			&nbsp;
			{{ this.pagination.of }}
			&nbsp;
			{{ totalRows }}
		  </span>
		  <span class='col-xs-4'>
			<button v-for='b in pagerButtons.backward' :disabled='b.disabled' :text='b.text' @click='changePage(b)'>{{ b.text }}</button>
			&nbsp;
			<input type='textbox' v-model='currentPage'></input>&nbsp;/&nbsp; {{ totalPages }}
			&nbsp;
			<button v-for='b in pagerButtons.forward' :disabled='b.disabled' :text='b.text' @click='changePage(b)'>{{ b.text }}</button>
		  </span>
		  <span class='col-xs-4 pull-right'>
			{{ pagination.perPage }}
			<input type='textbox' v-model='currentSize' />
		  </span>
	  </div>
  </div>
</script>

<script lang='javascript'>
    String.prototype.trimChar = function(charToRemove) {
        if(charToRemove){
            var str = this;
            while(str.charAt(0)==charToRemove) {
                str = str.substring(1);
            }

            while(str.charAt(str.length-1)==charToRemove) {
                str = str.substring(0,str.length-1);
            }
            return str;
        }
        else
            return this.trim();
    };

    String.prototype.caseInsensitiveMatch = function(strToMatch){
        return this.match(new RegExp(strToMatch, 'gi'));
    };

    // register the grid component
    Vue.component('vue-grid', {
        template: '#grid-template',
	
        props: {
            schema: Object,
            rows: Array,
            pager: Object,
            sort: Object,
            clientMode: Boolean,
        },
	
        data: function () {
            let sortOrders = {}
            this.schema.cols.forEach((col) => {
                if(this.sort && this.sort.key && col.key.caseInsensitiveMatch(this.sort.key) && this.sort.dir.caseInsensitiveMatch('desc'))
                sortOrders[col.key] = -1;
                else
                        sortOrders[col.key] = 1;
                });
		
            return {
                filterKey: '',
                sortKey: this.sort && this.sort.key ? this.sort.key : '',
                sortOrders: sortOrders,
                pagination: this.pager ? Object.assign({}, this.pager) : {allowed: false, total: 0,size:0,offset: 0,showing: 'showing',of: 'of', perPage: 'per page'},
                totalRows: this.pager ? (this.clientMode ? this.rows.length : this.pager.total) : 0
            }
        },
	
        computed: {
	
            currentPage: {
                get: function(){return this.pagination.offset + 1;},
                set: function(newValue){
                    if(newValue && newValue != '' && !isNaN(newValue) && newValue <= this.totalPages){
                        this.pagination.offset = newValue > 0 ? newValue -1 : 0;
                        this.updateGrid();//update grid view.
                    }
                }
            },
		
            totalPages: function(){
                return Math.ceil(this.totalRows/this.pagination.size);
            },
		
            currentSize:{
                get: function(){
                    return this.pagination.size;
                },
			
                set: function(newValue){
                    if(newValue && newValue != '' && !isNaN(newValue)){
                        this.pagination.size = parseInt(newValue > this.totalRows ? this.totalRows : newValue);
                        this.pagination.offset = 0;//reset page number when size is changed.
                        this.updateGrid();//update grid view.
                    }
                }
            },
	
            paginationAllowed: function(){
                return this.pagination.allowed && (this.totalRows > this.pagination.size || !this.clientMode);
            },
		
            fromRow: function(){
                return this.pagination.offset * this.pagination.size + 1;
            },
		
            toRow: function(){
                return (this.pagination.offset + 1) * this.pagination.size > this.totalRows ? this.totalRows :  (this.pagination.offset + 1) * this.pagination.size;
            },
		
            pagerButtons: function(){
                /*let buttons = [
				    {text: 'first', offset: 0, disabled: this.pagination.offset == 0 },
				    {text: 'previous', offset: this.pagination.offset - 1, disabled: this.pagination.offset == 0 }
			    ];
			
			    /*
			    for(let x = 0; x < (this.totalPages > 2 ? 3 : 2); x++){
				    let q = x + this.pagination.offset;
				    q = q + (this.pagination.offset + 1 == this.totalPages ? -1 : 0);
				    if(q < this.totalPages){
					    buttons.push({text: q + 1, offset: q, disabled: this.pagination.offset == q, selected: this.pagination.offset == q});
				    }
			    }
			    buttons.push({text: 'next', offset: this.pagination.offset + 1, disabled: this.pagination.offset + 1 == this.totalPages });
			    buttons.push({text: 'last', offset: this.totalPages-1, disabled: this.pagination.offset + 1 == this.totalPages });
			    return buttons;*/
			
                return {
                    backward:[
					    {text: 'first', offset: 0, disabled: this.pagination.offset == 0 },
					    {text: 'previous', offset: this.pagination.offset - 1, disabled: this.pagination.offset == 0 }],
                    forward: [
					    {text: 'next', offset: this.pagination.offset + 1, disabled: this.pagination.offset + 1 == this.totalPages },
					    {text: 'last', offset: this.totalPages-1, disabled: this.pagination.offset + 1 == this.totalPages }
                    ]
                }				
            },
		    
            viewableCols: function () {
                let cols = this.schema.cols;
                cols = cols.filter(col =>  { return col.viewable; });
                return cols;
            },

            searchableCols: function () {
                let cols = this.schema.cols;
                cols = cols.filter(col =>  { return col.searchable; });
                return cols;
            },
		
            sortableCols: function () {
                let cols = this.schema.cols;
                cols = cols.filter(col =>  { return col.sortable; });
                return cols;
            },
		
            filteredRows: function () {
                let rows = this.rows;
                if(this.clientMode){
                    let sortKey = this.sortKey
                    let order = this.sortOrders[sortKey] || 1
                    if (this.filterKey && this.filterKey != '') {
                            rows = rows.filter((row) => {
                                return this.viewableCols.filter((c) => {
                                    return c.searchable && String(row.data[c.key].value).toLowerCase().indexOf(this.filterKey) > -1;
                            }).length > 0;
                        });
                    }
			  
                    if (!sortKey && this.sortableCols.length > 0) {
                        this.sortKey = this.sortableCols[0].key
                        this.sortOrders[this.sortableCols[0].key] = 1;
                    }
			  
                    if(sortKey){
                        rows = rows.slice().sort(function (a, b) {
                            a = a.data[sortKey].value;
                            b = b.data[sortKey].value;
                            return (a === b ? 0 : a > b ? 1 : -1) * order
                        });
                    }
			  
                    this.totalRows = this.clientMode ? rows.length : this.pagination.total;
			  
                    if(this.paginationAllowed && this.clientMode){
                        let startIndex = this.pagination.size * this.pagination.offset;
                        let endIndex = startIndex + parseInt(this.pagination.size);
                        rows = rows.slice(startIndex, endIndex > rows.length ? rows.length : endIndex);
                    }
                }
                return rows;
            }
        },
	
        filters: {
                capitalize: function (str) {
                    return str.charAt(0).toUpperCase() + str.slice(1)
                }
        },
	
        watch: {
                rows: function(newRows, oldRows){
                    this.suppressExtended();
                    this.schema.cols.forEach(c => c.allSelected = false);
                }
        },
	
        methods: {
		
            updateGrid: function(){
                if(!this.clientMode){ //if not client mode for search, sort and pagination then raise event to parent for data update
                    this.$emit('state-changed', {
                        sort: {key: this.sortKey, dir: this.sortOrders[this.sortKey] > 0 ? 'asc' : 'desc'},
                        page: {size: this.currentSize, current: this.currentPage - 1} //current page and current size represent updated selections
                    });
                }
            },
		
            changePage: function(b){
                if(!b.disabled){
                    this.pagination.offset = b.offset;
                    this.updateGrid();
                }
            },
		
            sortBy: function (c) {
                if(c.sortable){
                    this.sortKey = c.key
                    this.sortOrders[c.key] = this.sortOrders[c.key] * -1;
                    this.updateGrid();
                }
            },

            isClickable: function(c){
                return ((c.link && c.link.uri && c.link.uri != '') || (c.callback && c.callback.event && c.callback.event != ''))  
            },

            toggleSelectCol: function(c) {
                this.rows.filter(r => !(r.data[c.key].disabled && r.data[c.key].disabled.caseInsensitiveMatch('disabled'))).forEach((r) => {
                        r.data[c.key].value = c.allSelected;
                });
                if(c.allSelected)
                    this.$emit('col-selected',{ col: c, rows: this.rows });
            },
		
            toggleSelect: function(c, r){
                if(r.data[c.key].value){
                    c.allSelected = this.rows.filter((row) => { return !row.data[c.key].value }).length > 0 ? false : true;
                    this.$emit('row-selected',{ col: c, row: r });
                }
                else
                    c.allSelected = false;
            },
		
            getLink: function(c, r){
                if(c.link && c.link.uri)
                {
                    var link = c.link.uri;
                    link = link + (link.match(/\?/) ? '&' : '?');
                    c.link.queryStrs.forEach((qs) => {
                        link = link + qs.name + '=' + r.data[qs.key].value + '&';
                    });
                    link = link.trimChar('&');
                    return link;
                }
                else
				    return 'javascript:void(0)';
            },

            getCallbackInfo: function(c,r){
                if(c.callback){
                    var callbackData = {};
                    if(c.callback.argKeys){
                        c.callback.argKeys.forEach(key => {
                            callbackData[key] = r.data[key].value;
                        });
                    }
                return {event: c.callback.event, args: callbackData};
                }
            },

            callbackHandler: function(callbackData){
                //console.log(callbackData);
                window[callbackData.event].apply(null, callbackData.args);
            },
		
            suppressExtended: function(){
                this.rows.forEach((r) => {
                    if(r.extended){
                        r.extended.visible = false;
                    }
                });
            }
        }
    });   
</script>